question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Is there a workaround for preventing `pytest` from dumping the source of `raise_pep_call_exception()` to stdout for every `@beartype`-caused test failure?

See original GitHub issue

Consider this source file:

from beartype import beartype

@beartype
def fn() -> int:
    return ""

def test_fn():
    fn()

If you save this and call it angry.py, and then run pytest angry.py, you should see something like this:

============================= test session starts ==============================
platform linux -- Python 3.9.11, pytest-7.1.1, pluggy-1.0.0
rootdir: /home/user
plugins: mock-3.7.0
collected 1 item

angry.py F                                                               [100%]

=================================== FAILURES ===================================
___________________________________ test_fn ____________________________________

    def test_fn():
>       fn()

angry.py:8: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
<@beartype(angry.fn) at 0x7f488f91a1f0>:17: in fn
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

func = <function fn at 0x7f488f91a1f0>, pith_name = 'return', pith_value = ''
random_int = None

    def raise_pep_call_exception(
        # Mandatory parameters.
        func: Callable,
        pith_name: str,
        pith_value: object,
    
        # Optional parameters.
        random_int: Optional[int] = None,
    ) -> None:
        '''
        Raise a human-readable exception detailing the failure of the parameter
        with the passed name *or* return value if this name is the magic string
        ``return`` of the passed decorated function fails to satisfy the
        PEP-compliant type hint annotated on this parameter or return value.
    
        Design
        ----------
        The :mod:`beartype` package actually implements two parallel PEP-compliant
        runtime type-checkers, each complementing the other by providing
        functionality unsuited for the other. These are:
    
        * The :mod:`beartype._decor._code._pep` submodule, dynamically generating
          optimized PEP-compliant runtime type-checking code embedded in the body
          of the wrapper function wrapping the decorated callable. For both
          efficiency and maintainability, that code only tests whether or not a
          parameter passed to that callable or value returned from that callable
          satisfies a PEP-compliant annotation on that callable; that code does
          *not* raise human-readable exceptions in the event that value fails to
          satisfy that annotation. Instead, that code defers to...
        * This function, performing unoptimized PEP-compliant runtime type-checking
          generically applicable to all wrapper functions. The aforementioned
          code calls this function only in the event that value fails to satisfy
          that annotation, in which case this function then raises a human-readable
          exception after discovering the underlying cause of this type failure by
          recursively traversing that value and annotation. While efficiency is the
          foremost focus of this package, efficiency is irrelevant during exception
          handling -- which typically only occurs under infrequent edge cases.
          Likewise, while raising this exception *would* technically be feasible
          from the aforementioned code, doing so proved sufficiently non-trivial,
          fragile, and ultimately unmaintainable to warrant offloading to this
          function universally callable from all wrapper functions.
    
        Parameters
        ----------
        func : CallableTypes
            Decorated callable to raise this exception from.
        pith_name : str
            Either:
    
            * If the object failing to satisfy this hint is a passed parameter, the
              name of this parameter.
            * Else, the magic string ``return`` implying this object to be the
              value returned from this callable.
        pith_value : object
            Passed parameter or returned value failing to satisfy this hint.
        random_int: Optional[int]
            **Pseudo-random integer** (i.e., unsigned 32-bit integer
            pseudo-randomly generated by the parent :func:`beartype.beartype`
            wrapper function in type-checking randomly indexed container items by
            the current call to that function) if that function generated such an
            integer *or* ``None`` otherwise (i.e., if that function generated *no*
            such integer). Note that this parameter critically governs whether this
            exception handler runs in constant or linear time. Specifically, if
            this parameter is:
    
            * An integer, this handler runs in **constant time.** Since there
              exists a one-to-one relation between this integer and the random
              container item(s) type-checked by the parent
              :func:`beartype.beartype` wrapper function, receiving this integer
              enables this handler to efficiently re-type-check the same random
              container item(s) type-checked by the parent in constant time rather
              type-checking all container items in linear time.
            * ``None``, this handler runs in **linear time.**
    
            Defaults to ``None``, implying this exception handler runs in linear
            time by default.
    
        Raises
        ----------
        BeartypeCallHintParamViolation
            If the object failing to satisfy this hint is a parameter.
        BeartypeCallHintReturnViolation
            If the object failing to satisfy this hint is a return value.
        BeartypeDecorHintPepException
            If the type hint annotating this object is *not* PEP-compliant.
        _BeartypeCallHintPepRaiseException
            If the parameter or return value with the passed name is unannotated.
        _BeartypeCallHintPepRaiseDesynchronizationException
            If this pith actually satisfies this hint, implying either:
    
            * The parent wrapper function generated by the :mod:`beartype.beartype`
              decorator type-checking this pith triggered a false negative by
              erroneously misdetecting this pith as failing this type check.
            * This child helper function re-type-checking this pith triggered a
              false positive by erroneously misdetecting this pith as satisfying
              this type check when in fact this pith fails to do so.
        '''
        assert callable(func), f'{repr(func)} uncallable.'
        assert isinstance(pith_name, str), f'{repr(pith_name)} not string.'
        assert isinstance(random_int, NoneTypeOr[int]), (
            f'{repr(random_int)} neither integer nor "None".')
        # print('''raise_pep_call_exception(
        #     func={!r},
        #     pith_name={!r},
        #     pith_value={!r}',
        # )'''.format(func, pith_name, pith_value))
    
        # Type of exception to be raised.
        exception_cls: TypeException = None  # type: ignore[assignment]
    
        # Human-readable label describing this parameter or return value.
        exception_prefix: str = None  # type: ignore[assignment]
    
        # If the name of this parameter is the magic string implying the passed
        # object to be a return value, set the above local variables appropriately.
        if pith_name == 'return':
            exception_cls = BeartypeCallHintReturnViolation
            exception_prefix = prefix_callable_decorated_return_value(
                func=func, return_value=pith_value)
        # Else, the passed object is a parameter. In this case, set the above local
        # variables appropriately.
        else:
            exception_cls = BeartypeCallHintParamViolation
            exception_prefix = prefix_callable_decorated_arg_value(
                func=func,
                arg_name=pith_name,
                arg_value=pith_value,
            )
    
        # If this parameter or return value is unannotated, raise an exception.
        #
        # Note that this should *NEVER* occur, as the caller guarantees this
        # parameter or return value to be annotated. Nonetheless, since callers
        # could deface the "__annotations__" dunder dictionary without our
        # knowledge or permission, precautions are warranted.
        if pith_name not in func.__annotations__:
            raise _BeartypeCallHintPepRaiseException(
                f'{exception_prefix}unannotated.')
        # Else, this parameter or return value is annotated.
    
        # PEP-compliant type hint annotating this parameter or return value.
        hint = func.__annotations__[pith_name]
    
        # If this is *NOT* the PEP 484-compliant "typing.NoReturn" type hint
        # permitted *ONLY* as a return annotation, this is a standard type hint
        # generally supported by both parameters and return values. In this case...
        if hint is not NoReturn:
            # If type hint is *NOT* a supported type hint, raise an exception.
            die_unless_hint(hint=hint, exception_prefix=exception_prefix)
            # Else, this type hint is supported.
    
        # Human-readable string describing the failure of this pith to satisfy this
        # hint if this pith fails to satisfy this hint *OR* "None" otherwise (i.e.,
        # if this pith satisfies this hint).
        exception_cause = CauseSleuth(
            func=func,
            pith=pith_value,
            hint=hint,
            cause_indent='',
            exception_prefix=exception_prefix,
            random_int=random_int,
        ).get_cause_or_none()
    
        # If this pith does *NOT* satisfy this hint...
        if exception_cause:
            # This failure suffixed by a period if *NOT* yet suffixed by a period.
            exception_cause_suffixed = suffix_unless_suffixed(
                text=exception_cause, suffix='.')
    
            # Raise an exception of the desired class embedding this cause.
>           raise exception_cls(  # type: ignore[misc]
                f'{exception_prefix}violates type hint {repr(hint)}, as '
                f'{exception_cause_suffixed}'
            )
E           beartype.roar.BeartypeCallHintReturnViolation: @beartyped fn() return '' violates type hint <class 'int'>, as '' not instance of int.

conda/envs/anki/lib/python3.9/site-packages/beartype/_decor/_error/errormain.py:301: BeartypeCallHintReturnViolation
=========================== short test summary info ============================
FAILED angry.py::test_fn - beartype.roar.BeartypeCallHintReturnViolation: @be...
============================== 1 failed in 0.04s ===============================

Is there any way of suppressing the bit where it dumps this rather tall function to stdout/stderr? What I’d like to see is maybe an opt-in setting or something where you can make it look like this:

============================= test session starts ==============================
platform linux -- Python 3.9.11, pytest-7.1.1, pluggy-1.0.0
rootdir: /home/user
plugins: mock-3.7.0
collected 1 item

angry.py F                                                               [100%]

=================================== FAILURES ===================================
___________________________________ test_fn ____________________________________

    def test_fn():
>       fn()

angry.py:8: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
<@beartype(angry.fn) at 0x7f488f91a1f0>:17: in fn
    ???
    ...
E           beartype.roar.BeartypeCallHintReturnViolation: @beartyped fn() return '' violates type hint <class 'int'>, as '' not instance of int.

conda/envs/anki/lib/python3.9/site-packages/beartype/_decor/_error/errormain.py:301: BeartypeCallHintReturnViolation
=========================== short test summary info ============================
FAILED angry.py::test_fn - beartype.roar.BeartypeCallHintReturnViolation: @be...
============================== 1 failed in 0.04s ===============================

Occasionally I break something in a project and when I run the tests, I get about 40 of these errors and you know

40 * TALL == VERY_TALL

Perhaps this is not something that beartype should support, but if anyone has got an idea of whether or not this is even possible, please let me know!

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:2
  • Comments:8 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
langfieldcommented, Jun 28, 2022

This works perfectly:

============================= test session starts ==============================
platform linux -- Python 3.10.4, pytest-7.1.2, pluggy-1.0.0
rootdir: /home/user
collected 2 items

angry.py FF                                                              [100%]

=================================== FAILURES ===================================
___________________________________ test_fn ____________________________________

    def test_fn():
>       fn()

angry.py:13: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

__beartype_func = <function fn at 0x7f4f1b476dd0>
__beartype_raise_exception = <function raise_pep_call_exception at 0x7f4f1b28f880>
args = (), kwargs = {}, __beartype_pith_0 = ''

>   ??? 
E   beartype.roar.BeartypeCallHintReturnViolation: @beartyped angry.fn() return ''
violates type hint <class 'int'>, as str '' not instance of int.

<@beartype(angry.fn) at 0x7f4f1b476dd0>:17: BeartypeCallHintReturnViolation
___________________________________ test_gn ____________________________________

    def test_gn():
>       gn([0, 1, 2, "3"], True)

angry.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

__beartype_func = <function gn at 0x7f4f1b3125f0>
__beartype_raise_exception = <function raise_pep_call_exception at 0x7f4f1b28f880>
__beartype_getrandbits = <built-in method getrandbits of Random object at 0x5643aaef99e0>
args = ([0, 1, 2, '3'], True), kwargs = {}, __beartype_random_int = 1510265183
__beartype_args_len = 2, __beartype_pith_0 = [0, 1, 2, '3']

>   ??? 
E   beartype.roar.BeartypeCallHintParamViolation: @beartyped angry.gn() parameter
good=[0, 1, 2, '3'] violates type hint list[int], as list index 3 item str '3' not instance of int.

<@beartype(angry.gn) at 0x7f4f1b3125f0>:31: BeartypeCallHintParamViolation
=========================== short test summary info ============================
FAILED angry.py::test_fn - beartype.roar.BeartypeCallHintReturnViolation: @be...
FAILED angry.py::test_gn - beartype.roar.BeartypeCallHintParamViolation: @bea...
============================== 2 failed in 0.04s ===============================

Source file:

from beartype import beartype
from beartype.typing import List

@beartype
def fn() -> int:
    return ""

@beartype
def gn(good: List[int], night: bool) -> int:
    return 0

def test_fn():
    fn()

def test_gn():
    gn([0, 1, 2, "3"], True)

Patch used, exactly as you described:

diff --git a/beartype/_decor/_error/errormain.py b/beartype/_decor/_error/errormain.py
index da519b1..e8a77f6 100644
--- a/beartype/_decor/_error/errormain.py
+++ b/beartype/_decor/_error/errormain.py
@@ -66,8 +66,10 @@ This private submodule is *not* intended for importation by downstream callers.
 #  containers in the exact same order as visited by our testing algorithm.
 
 # ....................{ IMPORTS                            }....................
+import operator
 from beartype.meta import URL_ISSUES
 from beartype.roar._roarexc import (
+    BeartypeCallHintViolation,
     BeartypeCallHintParamViolation,
     BeartypeCallHintReturnViolation,
     _BeartypeCallHintPepRaiseException,
@@ -228,6 +230,8 @@ def raise_pep_call_exception(
           false positive by erroneously misdetecting this pith as satisfying
           this type check when in fact this pith fails to do so.
     '''
+    __tracebackhide__ = operator.methodcaller(
+        'errisinstance', BeartypeCallHintViolation)
     assert callable(func), f'{repr(func)} uncallable.'
     assert isinstance(pith_name, str), f'{repr(pith_name)} not string.'
     assert isinstance(random_int, NoneTypeOr[int]), (

All tests pass, mypy and pyright are happy. Nice work, dude. That was lightning fast!

1reaction
langfieldcommented, Jun 30, 2022

Note a possibly typo: the commit message references #140 but this is #142.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to disable pytest dumping out source code?
However, something when the error is raised from another library, it still dumps the function source code flood the output. Is it possible...
Read more >
"--show-capture=no" option still capture teardown logs. #3816
Hello, I wanted to use --show-capture=no option to hide the captured log. Environment: platform linux -- Python 3.6.5, pytest-3.5.1, ...
Read more >
Allow pytest to be run without of capture : PY-35973
Indeed, output is captured. To disable it, you may pass JB_DISABLE_BUFFERING env. variable. But in this case, test output may go to the...
Read more >
pytest: Save full error output to file | by Alexander Hultnér
I recently had a problem with some rare occurring failures in tests, they happened at a rate of about 1 time per 5000...
Read more >
Step 9 - Pytest tutorial: Testing Exceptions - YouTube
Write tests that ensure exceptions are raised when expected.00:00 - Start00:25 - Write a test that triggers an error and expects an ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found