Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

during asyncio tests set a default loop exception_handler that warns (similar to -p unraisablehook) #205

Closed
graingert opened this issue Feb 22, 2021 · 1 comment · Fixed by #225
Labels
enhancement New feature or request
Milestone

Comments

@graingert
Copy link
Collaborator

graingert commented Feb 22, 2021

consider the following test:

import pytest
import asyncio

@pytest.mark.anyio
@pytest.mark.parameterize("anyio_backend", ["asyncio"])
async def test_foo():
    def raise_():
        raise Exception

    asyncio.get_running_loop().call_soon(raise_)
    await asyncio.sleep(0)

it passes cleanly.

However if you explicitly collect any errors:

import pytest
import asyncio

@pytest.mark.anyio
@pytest.mark.parameterize("anyio_backend", ["asyncio"])
async def test_foo():
    loop = asyncio.get_running_loop()
    old_exception_handler = loop.get_exception_handler()
    exceptions = []

    def exception_handler(*args, **kwargs):
        exceptions.append((args, kwargs))

    loop.set_exception_handler(exception_handler)
    try:
        def raise_():
            raise Exception

        asyncio.get_running_loop().call_soon(raise_)
        await asyncio.sleep(0)
        assert exceptions == []
    finally:
        loop.set_exception_handler(old_exception_handler)
=================================== test session starts ====================================
platform linux -- Python 3.9.2, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /home/graingert/projects/anyio, configfile: pyproject.toml
plugins: hypothesis-6.2.0, asyncio-0.14.0, anyio-2.1.0.post21+dirty
collected 1 item                                                                           

ham.py F                                                                             [100%]

========================================= FAILURES =========================================
_________________________________________ test_foo _________________________________________
ham.py:20: in test_foo
    assert exceptions == []
E   AssertionError: assert [((<_UnixSele...py:15'}), {})] == []
E     Left contains one more item: ((<_UnixSelectorEventLoop running=True closed=False debug=False>, {'exception': Exception(), 'handle': <Handle test_fo...y:15>, 'message': 'Exception in callback test_foo.<locals>.raise_() at /home/graingert/projects/anyio/ham.py:17'}), {})
E     Use -v to get the full diff
==================================== 1 failed in 0.04s ====================================

pytest-dev/pytest-asyncio#205
https://github.com/pytest-dev/pytest/blob/master/src/_pytest/unraisableexception.py#L62-L78

@agronholm agronholm added this to the 3.0.0 milestone Feb 24, 2021
@agronholm agronholm added the enhancement New feature or request label Feb 24, 2021
agronholm added a commit that referenced this issue Mar 2, 2021
It will now raise all the capture exceptions if the test function completes without raising any exceptions of its own.

Closes #205.
@graingert
Copy link
Collaborator Author

graingert commented Jul 25, 2021

it turns out I actually want to set the handler to throw the error much earlier:

async def _async_raise(e):
    raise e

@contextlib.asynccontextmanager
async def exception_handler() -> AsyncGenerator[None, None]:
    async with anyio.create_task_group() as tg:
        def exception_handler(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None:
            tg.start_soon(_async_raise, context['exception'])

        loop = asyncio.get_running_loop()
        old_handler = loop.get_exception_handler()
        loop.set_exception_handler(exception_handler)
        try:
            yield
        finally:
            loop.set_exception_handler(old_handler)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants