def always_iterable(item):
	"""
	Given an object, always return an iterable. If the item is not
	already iterable, return a tuple containing only the item. If item is
	None, an empty iterable is returned.

	>>> always_iterable([1,2,3])
	[1, 2, 3]
	>>> always_iterable('foo')
	('foo',)
	>>> always_iterable(None)
	()
	>>> always_iterable(range(10))
	range(0, 10)
	>>> def _test_func(): yield "I'm iterable"
	>>> print(next(always_iterable(_test_func())))
	I'm iterable

	Although mappings are iterable, treat each like a singleton, as
	it's more like an object than a sequence.

	>>> always_iterable(dict(a=1))
	({'a': 1},)
	"""
	return (
		(item,)
		if isinstance(item, collections.Mapping)
		else more.always_iterable(item)
	)
Ejemplo n.º 2
0
    def merge_styles(self, offline: bool) -> Iterator[Fuss]:
        """Merge one or multiple style files."""
        config = self.read_configuration()

        # pylint: disable=import-outside-toplevel
        from nitpick.style import StyleManager

        style = StyleManager(self, offline, config.cache)
        base = config.file.expanduser().resolve().as_uri(
        ) if config.file else None
        style_errors = list(
            style.find_initial_styles(peekable(always_iterable(config.styles)),
                                      base))
        if style_errors:
            raise QuitComplainingError(style_errors)

        self.style_dict = style.merge_toml_dict()

        from nitpick.flake8 import NitpickFlake8Extension

        minimum_version = search_json(self.style_dict,
                                      NITPICK_MINIMUM_VERSION_JMEX, None)
        logger.debug(f"Minimum version: {minimum_version}")
        if minimum_version and version_to_tuple(
                NitpickFlake8Extension.version) < version_to_tuple(
                    minimum_version):
            yield Reporter().make_fuss(
                ProjectViolations.MINIMUM_VERSION,
                project=PROJECT_NAME,
                expected=minimum_version,
                actual=NitpickFlake8Extension.version,
            )

        self.nitpick_section = self.style_dict.get("nitpick", {})
        self.nitpick_files_section = self.nitpick_section.get("files", {})
Ejemplo n.º 3
0
def always_iterable(item):
	"""
	Given an object, always return an iterable. If the item is not
	already iterable, return a tuple containing only the item. If item is
	None, an empty iterable is returned.

	>>> always_iterable([1,2,3])
	<list_iterator...>
	>>> always_iterable('foo')
	<tuple_iterator...>
	>>> always_iterable(None)
	<tuple_iterator...>
	>>> always_iterable(range(10))
	<range_iterator...>
	>>> def _test_func(): yield "I'm iterable"
	>>> print(next(always_iterable(_test_func())))
	I'm iterable

	Although mappings are iterable, treat each like a singleton, as
	it's more like an object than a sequence.

	>>> next(always_iterable(dict(a=1)))
	{'a': 1}
	"""
	base_types = six.text_type, bytes, collections.abc.Mapping
	return more.always_iterable(item, base_type=base_types)
Ejemplo n.º 4
0
        def default_from_config(keys, defaults):
            _keys = list(always_iterable(keys))
            _defaults = list(always_iterable(defaults))

            def getter(field):
                ftype, fname = self.data_source._determine_fields(field)[0]
                ret = [
                    ytcfg.get_most_specific("plot",
                                            ftype,
                                            fname,
                                            key,
                                            fallback=default)
                    for key, default in zip(_keys, _defaults)
                ]
                if len(ret) == 1:
                    return ret[0]
                return ret

            return getter
Ejemplo n.º 5
0
    def assertStatus(self, status, msg=None):
        """Fail if self.status != status.

        status may be integer code, exact string status, or
        iterable of allowed possibilities.
        """
        if any(map(self.status_matches, always_iterable(status))):
            return

        tmpl = 'Status {self.status} does not match {status}'
        msg = msg or tmpl.format(**locals())
        self._handlewebError(msg)
Ejemplo n.º 6
0
    def assertStatus(self, status, msg=None):
        """Fail if self.status != status.

        status may be integer code, exact string status, or
        iterable of allowed possibilities.
        """
        if any(map(self.status_matches, always_iterable(status))):
            return

        tmpl = 'Status {self.status} does not match {status}'
        msg = msg or tmpl.format(**locals())
        self._handlewebError(msg)
Ejemplo n.º 7
0
def radar2mat(radars, axis=1, as_dict=False, **kwargs):
    '''Render one or more radar files as a 4d array'''

    #from IPython.core.debugger import set_trace; set_trace()

    radars = always_iterable(radars)
    results = [radar2mat_single(r, **kwargs) for r in radars]

    data = np.concatenate([r[0] for r in results], axis=axis)
    coords = [r[1:] for r in results]

    # coords[i] = (fields, elev, y, x) or (fields, elev, range, azimuth)

    combined_coords = list(coords[0])
    combined_coords[axis] = np.concatenate([r[axis] for r in coords])

    if as_dict:
        fields = coords[1]
        data = {f: v for f, v in zip(fields, data)}

    return (data, ) + tuple(combined_coords)
Ejemplo n.º 8
0
    def _simulate_cli(self, command: str, expected_str_or_lines: StrOrList = None, *args: str, exit_code: int = None):
        """Simulate the CLI by invoking a click command.

        1. If the command raised an exception that was not a common "system exit",
            this method will re-raise it, so the test can be fixed.
        """
        result = CliRunner().invoke(nitpick_cli, ["--project", str(self.root_dir), command, *args])

        # 1.
        if result.exception and not isinstance(result.exception, SystemExit):
            raise result.exception

        actual: list[str] = result.output.splitlines()

        if isinstance(expected_str_or_lines, str):
            expected = dedent(expected_str_or_lines).strip().splitlines()
        else:
            expected = list(always_iterable(expected_str_or_lines))

        compare(actual=result.exit_code, expected=exit_code or 0)

        return result, actual, expected
Ejemplo n.º 9
0
def raises(expected_exception, *args, **kwargs):
    r"""
    Assert that a code block/function call raises ``expected_exception``
    and raise a failure exception otherwise.

    :arg message: if specified, provides a custom failure message if the
        exception is not raised
    :arg match: if specified, asserts that the exception matches a text or regex

    This helper produces a ``ExceptionInfo()`` object (see below).

    You may use this function as a context manager::

        >>> with raises(ZeroDivisionError):
        ...    1/0

    .. versionchanged:: 2.10

    In the context manager form you may use the keyword argument
    ``message`` to specify a custom failure message::

        >>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
        ...    pass
        Traceback (most recent call last):
          ...
        Failed: Expecting ZeroDivisionError

    .. note::

       When using ``pytest.raises`` as a context manager, it's worthwhile to
       note that normal context manager rules apply and that the exception
       raised *must* be the final line in the scope of the context manager.
       Lines of code after that, within the scope of the context manager will
       not be executed. For example::

           >>> value = 15
           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...     assert exc_info.type == ValueError  # this will not execute

       Instead, the following approach must be taken (note the difference in
       scope)::

           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...
           >>> assert exc_info.type == ValueError


    Since version ``3.1`` you can use the keyword argument ``match`` to assert that the
    exception matches a text or regex::

        >>> with raises(ValueError, match='must be 0 or None'):
        ...     raise ValueError("value must be 0 or None")

        >>> with raises(ValueError, match=r'must be \d+$'):
        ...     raise ValueError("value must be 42")

    **Legacy forms**

    The forms below are fully supported but are discouraged for new code because the
    context manager form is regarded as more readable and less error-prone.

    It is possible to specify a callable by passing a to-be-called lambda::

        >>> raises(ZeroDivisionError, lambda: 1/0)
        <ExceptionInfo ...>

    or you can specify an arbitrary callable with arguments::

        >>> def f(x): return 1/x
        ...
        >>> raises(ZeroDivisionError, f, 0)
        <ExceptionInfo ...>
        >>> raises(ZeroDivisionError, f, x=0)
        <ExceptionInfo ...>

    It is also possible to pass a string to be evaluated at runtime::

        >>> raises(ZeroDivisionError, "f(0)")
        <ExceptionInfo ...>

    The string will be evaluated using the same ``locals()`` and ``globals()``
    at the moment of the ``raises`` call.

    .. currentmodule:: _pytest._code

    Consult the API of ``excinfo`` objects: :class:`ExceptionInfo`.

    .. note::
        Similar to caught exception objects in Python, explicitly clearing
        local references to returned ``ExceptionInfo`` objects can
        help the Python interpreter speed up its garbage collection.

        Clearing those references breaks a reference cycle
        (``ExceptionInfo`` --> caught exception --> frame stack raising
        the exception --> current frame stack --> local variables -->
        ``ExceptionInfo``) which makes Python keep all objects referenced
        from that cycle (including all local variables in the current
        frame) alive until the next cyclic garbage collection run. See the
        official Python ``try`` statement documentation for more detailed
        information.

    """
    __tracebackhide__ = True
    for exc in filterfalse(isclass, always_iterable(expected_exception, BASE_TYPE)):
        msg = (
            "exceptions must be old-style classes or"
            " derived from BaseException, not %s"
        )
        raise TypeError(msg % type(exc))

    message = "DID NOT RAISE {}".format(expected_exception)
    match_expr = None

    if not args:
        if "message" in kwargs:
            message = kwargs.pop("message")
        if "match" in kwargs:
            match_expr = kwargs.pop("match")
        if kwargs:
            msg = "Unexpected keyword arguments passed to pytest.raises: "
            msg += ", ".join(kwargs.keys())
            raise TypeError(msg)
        return RaisesContext(expected_exception, message, match_expr)
    elif isinstance(args[0], str):
        code, = args
        assert isinstance(code, str)
        frame = sys._getframe(1)
        loc = frame.f_locals.copy()
        loc.update(kwargs)
        # print "raises frame scope: %r" % frame.f_locals
        try:
            code = _pytest._code.Source(code).compile()
            py.builtin.exec_(code, frame.f_globals, loc)
            # XXX didn'T mean f_globals == f_locals something special?
            #     this is destroyed here ...
        except expected_exception:
            return _pytest._code.ExceptionInfo()
    else:
        func = args[0]
        try:
            func(*args[1:], **kwargs)
        except expected_exception:
            return _pytest._code.ExceptionInfo()
    fail(message)
Ejemplo n.º 10
0
def raises(expected_exception, *args, **kwargs):
    r"""
    Assert that a code block/function call raises ``expected_exception``
    and raise a failure exception otherwise.

    :arg message: if specified, provides a custom failure message if the
        exception is not raised
    :arg match: if specified, asserts that the exception matches a text or regex

    This helper produces a ``ExceptionInfo()`` object (see below).

    You may use this function as a context manager::

        >>> with raises(ZeroDivisionError):
        ...    1/0

    .. versionchanged:: 2.10

    In the context manager form you may use the keyword argument
    ``message`` to specify a custom failure message::

        >>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
        ...    pass
        Traceback (most recent call last):
          ...
        Failed: Expecting ZeroDivisionError

    .. note::

       When using ``pytest.raises`` as a context manager, it's worthwhile to
       note that normal context manager rules apply and that the exception
       raised *must* be the final line in the scope of the context manager.
       Lines of code after that, within the scope of the context manager will
       not be executed. For example::

           >>> value = 15
           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...     assert exc_info.type == ValueError  # this will not execute

       Instead, the following approach must be taken (note the difference in
       scope)::

           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...
           >>> assert exc_info.type == ValueError


    Since version ``3.1`` you can use the keyword argument ``match`` to assert that the
    exception matches a text or regex::

        >>> with raises(ValueError, match='must be 0 or None'):
        ...     raise ValueError("value must be 0 or None")

        >>> with raises(ValueError, match=r'must be \d+$'):
        ...     raise ValueError("value must be 42")

    **Legacy forms**

    The forms below are fully supported but are discouraged for new code because the
    context manager form is regarded as more readable and less error-prone.

    It is possible to specify a callable by passing a to-be-called lambda::

        >>> raises(ZeroDivisionError, lambda: 1/0)
        <ExceptionInfo ...>

    or you can specify an arbitrary callable with arguments::

        >>> def f(x): return 1/x
        ...
        >>> raises(ZeroDivisionError, f, 0)
        <ExceptionInfo ...>
        >>> raises(ZeroDivisionError, f, x=0)
        <ExceptionInfo ...>

    It is also possible to pass a string to be evaluated at runtime::

        >>> raises(ZeroDivisionError, "f(0)")
        <ExceptionInfo ...>

    The string will be evaluated using the same ``locals()`` and ``globals()``
    at the moment of the ``raises`` call.

    .. currentmodule:: _pytest._code

    Consult the API of ``excinfo`` objects: :class:`ExceptionInfo`.

    .. note::
        Similar to caught exception objects in Python, explicitly clearing
        local references to returned ``ExceptionInfo`` objects can
        help the Python interpreter speed up its garbage collection.

        Clearing those references breaks a reference cycle
        (``ExceptionInfo`` --> caught exception --> frame stack raising
        the exception --> current frame stack --> local variables -->
        ``ExceptionInfo``) which makes Python keep all objects referenced
        from that cycle (including all local variables in the current
        frame) alive until the next cyclic garbage collection run. See the
        official Python ``try`` statement documentation for more detailed
        information.

    """
    __tracebackhide__ = True
    for exc in filterfalse(isclass,
                           always_iterable(expected_exception, BASE_TYPE)):
        msg = ("exceptions must be old-style classes or"
               " derived from BaseException, not %s")
        raise TypeError(msg % type(exc))

    message = "DID NOT RAISE {}".format(expected_exception)
    match_expr = None

    if not args:
        if "message" in kwargs:
            message = kwargs.pop("message")
        if "match" in kwargs:
            match_expr = kwargs.pop("match")
        if kwargs:
            msg = "Unexpected keyword arguments passed to pytest.raises: "
            msg += ", ".join(kwargs.keys())
            raise TypeError(msg)
        return RaisesContext(expected_exception, message, match_expr)
    elif isinstance(args[0], str):
        code, = args
        assert isinstance(code, str)
        frame = sys._getframe(1)
        loc = frame.f_locals.copy()
        loc.update(kwargs)
        # print "raises frame scope: %r" % frame.f_locals
        try:
            code = _pytest._code.Source(code).compile()
            six.exec_(code, frame.f_globals, loc)
            # XXX didn't mean f_globals == f_locals something special?
            #     this is destroyed here ...
        except expected_exception:
            return _pytest._code.ExceptionInfo()
    else:
        func = args[0]
        try:
            func(*args[1:], **kwargs)
        except expected_exception:
            return _pytest._code.ExceptionInfo()
    fail(message)
Ejemplo n.º 11
0
def raises(  # noqa: F811
    expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
    *args: Any,
    match: Optional[Union[str, "Pattern"]] = None,
    **kwargs: Any
) -> Union["RaisesContext[_E]", Optional[_pytest._code.ExceptionInfo[_E]]]:
    r"""
    Assert that a code block/function call raises ``expected_exception``
    or raise a failure exception otherwise.

    :kwparam match: if specified, a string containing a regular expression,
        or a regular expression object, that is tested against the string
        representation of the exception using ``re.search``. To match a literal
        string that may contain `special characters`__, the pattern can
        first be escaped with ``re.escape``.

        __ https://docs.python.org/3/library/re.html#regular-expression-syntax


    .. currentmodule:: _pytest._code

    Use ``pytest.raises`` as a context manager, which will capture the exception of the given
    type::

        >>> with raises(ZeroDivisionError):
        ...    1/0

    If the code block does not raise the expected exception (``ZeroDivisionError`` in the example
    above), or no exception at all, the check will fail instead.

    You can also use the keyword argument ``match`` to assert that the
    exception matches a text or regex::

        >>> with raises(ValueError, match='must be 0 or None'):
        ...     raise ValueError("value must be 0 or None")

        >>> with raises(ValueError, match=r'must be \d+$'):
        ...     raise ValueError("value must be 42")

    The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the
    details of the captured exception::

        >>> with raises(ValueError) as exc_info:
        ...     raise ValueError("value must be 42")
        >>> assert exc_info.type is ValueError
        >>> assert exc_info.value.args[0] == "value must be 42"

    .. deprecated:: 4.1

        In the context manager form you may use the keyword argument
        ``message`` to specify a custom failure message that will be displayed
        in case the ``pytest.raises`` check fails. This has been deprecated as it
        is considered error prone as users often mean to use ``match`` instead.
        See :ref:`the deprecation docs <raises message deprecated>` for a workaround.

    .. note::

       When using ``pytest.raises`` as a context manager, it's worthwhile to
       note that normal context manager rules apply and that the exception
       raised *must* be the final line in the scope of the context manager.
       Lines of code after that, within the scope of the context manager will
       not be executed. For example::

           >>> value = 15
           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...     assert exc_info.type is ValueError  # this will not execute

       Instead, the following approach must be taken (note the difference in
       scope)::

           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...
           >>> assert exc_info.type is ValueError

    **Using with** ``pytest.mark.parametrize``

    When using :ref:`pytest.mark.parametrize ref`
    it is possible to parametrize tests such that
    some runs raise an exception and others do not.

    See :ref:`parametrizing_conditional_raising` for an example.

    **Legacy form**

    It is possible to specify a callable by passing a to-be-called lambda::

        >>> raises(ZeroDivisionError, lambda: 1/0)
        <ExceptionInfo ...>

    or you can specify an arbitrary callable with arguments::

        >>> def f(x): return 1/x
        ...
        >>> raises(ZeroDivisionError, f, 0)
        <ExceptionInfo ...>
        >>> raises(ZeroDivisionError, f, x=0)
        <ExceptionInfo ...>

    The form above is fully supported but discouraged for new code because the
    context manager form is regarded as more readable and less error-prone.

    .. note::
        Similar to caught exception objects in Python, explicitly clearing
        local references to returned ``ExceptionInfo`` objects can
        help the Python interpreter speed up its garbage collection.

        Clearing those references breaks a reference cycle
        (``ExceptionInfo`` --> caught exception --> frame stack raising
        the exception --> current frame stack --> local variables -->
        ``ExceptionInfo``) which makes Python keep all objects referenced
        from that cycle (including all local variables in the current
        frame) alive until the next cyclic garbage collection run. See the
        official Python ``try`` statement documentation for more detailed
        information.

    """
    __tracebackhide__ = True
    for exc in filterfalse(inspect.isclass,
                           always_iterable(expected_exception, BASE_TYPE)):
        msg = "exceptions must be derived from BaseException, not %s"
        raise TypeError(msg % type(exc))

    message = "DID NOT RAISE {}".format(expected_exception)

    if not args:
        if kwargs:
            msg = "Unexpected keyword arguments passed to pytest.raises: "
            msg += ", ".join(sorted(kwargs))
            msg += "\nUse context-manager form instead?"
            raise TypeError(msg)
        return RaisesContext(expected_exception, message, match)
    else:
        func = args[0]
        if not callable(func):
            raise TypeError("{!r} object (type: {}) must be callable".format(
                func, type(func)))
        try:
            func(*args[1:], **kwargs)
        except expected_exception as e:
            # We just caught the exception - there is a traceback.
            assert e.__traceback__ is not None
            return _pytest._code.ExceptionInfo.from_exc_info(
                (type(e), e, e.__traceback__))
    fail(message)
Ejemplo n.º 12
0
def raises(expected_exception, *args, **kwargs):
    r"""
    Assert that a code block/function call raises ``expected_exception``
    or raise a failure exception otherwise.

    :kwparam match: if specified, asserts that the exception matches a text or regex

    :kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message
        if the exception is not raised

    .. currentmodule:: _pytest._code

    Use ``pytest.raises`` as a context manager, which will capture the exception of the given
    type::

        >>> with raises(ZeroDivisionError):
        ...    1/0

    If the code block does not raise the expected exception (``ZeroDivisionError`` in the example
    above), or no exception at all, the check will fail instead.

    You can also use the keyword argument ``match`` to assert that the
    exception matches a text or regex::

        >>> with raises(ValueError, match='must be 0 or None'):
        ...     raise ValueError("value must be 0 or None")

        >>> with raises(ValueError, match=r'must be \d+$'):
        ...     raise ValueError("value must be 42")

    The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the
    details of the captured exception::

        >>> with raises(ValueError) as exc_info:
        ...     raise ValueError("value must be 42")
        >>> assert exc_info.type is ValueError
        >>> assert exc_info.value.args[0] == "value must be 42"

    .. deprecated:: 4.1

        In the context manager form you may use the keyword argument
        ``message`` to specify a custom failure message that will be displayed
        in case the ``pytest.raises`` check fails. This has been deprecated as it
        is considered error prone as users often mean to use ``match`` instead.

    .. note::

       When using ``pytest.raises`` as a context manager, it's worthwhile to
       note that normal context manager rules apply and that the exception
       raised *must* be the final line in the scope of the context manager.
       Lines of code after that, within the scope of the context manager will
       not be executed. For example::

           >>> value = 15
           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...     assert exc_info.type is ValueError  # this will not execute

       Instead, the following approach must be taken (note the difference in
       scope)::

           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...
           >>> assert exc_info.type is ValueError

    **Using with** ``pytest.mark.parametrize``

    When using :ref:`pytest.mark.parametrize ref`
    it is possible to parametrize tests such that
    some runs raise an exception and others do not.

    See :ref:`parametrizing_conditional_raising` for an example.

    **Legacy form**

    It is possible to specify a callable by passing a to-be-called lambda::

        >>> raises(ZeroDivisionError, lambda: 1/0)
        <ExceptionInfo ...>

    or you can specify an arbitrary callable with arguments::

        >>> def f(x): return 1/x
        ...
        >>> raises(ZeroDivisionError, f, 0)
        <ExceptionInfo ...>
        >>> raises(ZeroDivisionError, f, x=0)
        <ExceptionInfo ...>

    The form above is fully supported but discouraged for new code because the
    context manager form is regarded as more readable and less error-prone.

    .. note::
        Similar to caught exception objects in Python, explicitly clearing
        local references to returned ``ExceptionInfo`` objects can
        help the Python interpreter speed up its garbage collection.

        Clearing those references breaks a reference cycle
        (``ExceptionInfo`` --> caught exception --> frame stack raising
        the exception --> current frame stack --> local variables -->
        ``ExceptionInfo``) which makes Python keep all objects referenced
        from that cycle (including all local variables in the current
        frame) alive until the next cyclic garbage collection run. See the
        official Python ``try`` statement documentation for more detailed
        information.

    """
    __tracebackhide__ = True
    for exc in filterfalse(isclass, always_iterable(expected_exception, BASE_TYPE)):
        msg = (
            "exceptions must be old-style classes or"
            " derived from BaseException, not %s"
        )
        raise TypeError(msg % type(exc))

    message = "DID NOT RAISE {}".format(expected_exception)
    match_expr = None

    if not args:
        if "message" in kwargs:
            message = kwargs.pop("message")
            warnings.warn(deprecated.RAISES_MESSAGE_PARAMETER, stacklevel=2)
        if "match" in kwargs:
            match_expr = kwargs.pop("match")
        if kwargs:
            msg = "Unexpected keyword arguments passed to pytest.raises: "
            msg += ", ".join(kwargs.keys())
            raise TypeError(msg)
        return RaisesContext(expected_exception, message, match_expr)
    elif isinstance(args[0], str):
        warnings.warn(deprecated.RAISES_EXEC, stacklevel=2)
        code, = args
        assert isinstance(code, str)
        frame = sys._getframe(1)
        loc = frame.f_locals.copy()
        loc.update(kwargs)
        # print "raises frame scope: %r" % frame.f_locals
        try:
            code = _pytest._code.Source(code).compile(_genframe=frame)
            six.exec_(code, frame.f_globals, loc)
            # XXX didn't mean f_globals == f_locals something special?
            #     this is destroyed here ...
        except expected_exception:
            return _pytest._code.ExceptionInfo.from_current()
    else:
        func = args[0]
        try:
            func(*args[1:], **kwargs)
        except expected_exception:
            return _pytest._code.ExceptionInfo.from_current()
    fail(message)
Ejemplo n.º 13
0
def raises(expected_exception, *args, **kwargs):
    r"""
    Assert that a code block/function call raises ``expected_exception``
    or raise a failure exception otherwise.

    :kwparam match: if specified, a string containing a regular expression,
        or a regular expression object, that is tested against the string
        representation of the exception using ``re.search``. To match a literal
        string that may contain `special characters`__, the pattern can
        first be escaped with ``re.escape``.

    __ https://docs.python.org/3/library/re.html#regular-expression-syntax

    :kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message
        if the exception is not raised. See :ref:`the deprecation docs <raises message deprecated>` for a workaround.

    .. currentmodule:: _pytest._code

    Use ``pytest.raises`` as a context manager, which will capture the exception of the given
    type::

        >>> with raises(ZeroDivisionError):
        ...    1/0

    If the code block does not raise the expected exception (``ZeroDivisionError`` in the example
    above), or no exception at all, the check will fail instead.

    You can also use the keyword argument ``match`` to assert that the
    exception matches a text or regex::

        >>> with raises(ValueError, match='must be 0 or None'):
        ...     raise ValueError("value must be 0 or None")

        >>> with raises(ValueError, match=r'must be \d+$'):
        ...     raise ValueError("value must be 42")

    The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the
    details of the captured exception::

        >>> with raises(ValueError) as exc_info:
        ...     raise ValueError("value must be 42")
        >>> assert exc_info.type is ValueError
        >>> assert exc_info.value.args[0] == "value must be 42"

    .. deprecated:: 4.1

        In the context manager form you may use the keyword argument
        ``message`` to specify a custom failure message that will be displayed
        in case the ``pytest.raises`` check fails. This has been deprecated as it
        is considered error prone as users often mean to use ``match`` instead.
        See :ref:`the deprecation docs <raises message deprecated>` for a workaround.

    .. note::

       When using ``pytest.raises`` as a context manager, it's worthwhile to
       note that normal context manager rules apply and that the exception
       raised *must* be the final line in the scope of the context manager.
       Lines of code after that, within the scope of the context manager will
       not be executed. For example::

           >>> value = 15
           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...     assert exc_info.type is ValueError  # this will not execute

       Instead, the following approach must be taken (note the difference in
       scope)::

           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...
           >>> assert exc_info.type is ValueError

    **Using with** ``pytest.mark.parametrize``

    When using :ref:`pytest.mark.parametrize ref`
    it is possible to parametrize tests such that
    some runs raise an exception and others do not.

    See :ref:`parametrizing_conditional_raising` for an example.

    **Legacy form**

    It is possible to specify a callable by passing a to-be-called lambda::

        >>> raises(ZeroDivisionError, lambda: 1/0)
        <ExceptionInfo ...>

    or you can specify an arbitrary callable with arguments::

        >>> def f(x): return 1/x
        ...
        >>> raises(ZeroDivisionError, f, 0)
        <ExceptionInfo ...>
        >>> raises(ZeroDivisionError, f, x=0)
        <ExceptionInfo ...>

    The form above is fully supported but discouraged for new code because the
    context manager form is regarded as more readable and less error-prone.

    .. note::
        Similar to caught exception objects in Python, explicitly clearing
        local references to returned ``ExceptionInfo`` objects can
        help the Python interpreter speed up its garbage collection.

        Clearing those references breaks a reference cycle
        (``ExceptionInfo`` --> caught exception --> frame stack raising
        the exception --> current frame stack --> local variables -->
        ``ExceptionInfo``) which makes Python keep all objects referenced
        from that cycle (including all local variables in the current
        frame) alive until the next cyclic garbage collection run. See the
        official Python ``try`` statement documentation for more detailed
        information.

    """
    __tracebackhide__ = True
    for exc in filterfalse(isclass,
                           always_iterable(expected_exception, BASE_TYPE)):
        msg = ("exceptions must be old-style classes or"
               " derived from BaseException, not %s")
        raise TypeError(msg % type(exc))

    message = "DID NOT RAISE {}".format(expected_exception)
    match_expr = None

    if not args:
        if "message" in kwargs:
            message = kwargs.pop("message")
            warnings.warn(deprecated.RAISES_MESSAGE_PARAMETER, stacklevel=2)
        if "match" in kwargs:
            match_expr = kwargs.pop("match")
        if kwargs:
            msg = "Unexpected keyword arguments passed to pytest.raises: "
            msg += ", ".join(sorted(kwargs))
            raise TypeError(msg)
        return RaisesContext(expected_exception, message, match_expr)
    elif isinstance(args[0], str):
        warnings.warn(deprecated.RAISES_EXEC, stacklevel=2)
        code, = args
        assert isinstance(code, str)
        frame = sys._getframe(1)
        loc = frame.f_locals.copy()
        loc.update(kwargs)
        # print "raises frame scope: %r" % frame.f_locals
        try:
            code = _pytest._code.Source(code).compile(_genframe=frame)
            exec(code, frame.f_globals, loc)
            # XXX didn't mean f_globals == f_locals something special?
            #     this is destroyed here ...
        except expected_exception:
            return _pytest._code.ExceptionInfo.from_current()
    else:
        func = args[0]
        try:
            func(*args[1:], **kwargs)
        except expected_exception:
            return _pytest._code.ExceptionInfo.from_current()
    fail(message)