-
-
Notifications
You must be signed in to change notification settings - Fork 25
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
How do I catch errors in tasks started from fixtures and abort tests(s) using cancel scope? #120
Comments
I'm not quite sure I'm following the details here, but this looks like a bug -- if a task in a fixture raises an exception then pytest-trio should automatically cancel everything and give you a traceback. |
For reference, running with
...and then it hangs. |
Here's a self-contained minimal version that still demonstrates the hang: # test_gh_120.py
import trio
import pytest
@pytest.fixture
async def hanging_fixture():
print("hanging_fixture:start")
await trio.Event().wait()
yield
print("hanging_fixture:end")
@pytest.fixture
async def exploding_fixture():
print("exploding_fixture:start")
raise Exception
yield
print("exploding_fixture:end")
@pytest.mark.trio
async def test_fails_right_away(exploding_fixture):
...
@pytest.mark.trio
async def test_fails_needs_some_scopes(exploding_fixture, hanging_fixture):
... Output:
|
Oh hmm, so apparently I set things up this way on purpose, at some point: pytest-trio/pytest_trio/plugin.py Lines 121 to 128 in 59da535
Maybe we should revisit that? |
The logic in the statements line 121 - 128 sound really solid to me and I can't disagree with any of it. In this specific case, the part Lets see if, by What seems to be happening here: the cancel scope never affects the As a result, Hence, a fix would be to apply a cancel scope, perhaps explicitly on explicit dependencies like the one built in this case - we would have to, think of a way to either cancel the If this does not make sense, let's figure out where we differ. |
What that comment is saying is: we only cancel the test function itself, the function called I think we can tweak this slightly: if the test hasn't started yet, we don't start it, and we treat it like it's failed and start unwinding fixtures backwards down the dependency tree. I guess I just never thought about this case :-) |
Ah! This is what I was missing. Logically it makes sense.
Makes sense!
Sure. I mean if you wanted to be picky, we can mark it as skipped with a reason "Fixtures failed" or similar (because we don't have information whether the test actually failed). However, this could be something I add on later after I work with you to figure out the fundamental cancel mechanics. So, would it be fair to say the "blocker" for this issue is that |
No, we can cancel it. We just need to wire things up in pytest-trio so that it does cancel it under this circumstance :-). I spent a few minutes looking at this yesterday and didn't get terribly far. The concurrent fixture setup code is hard to understand. I guess another way to solve this would be to remove the concurrent setup -- so then the first fixture would fail, and the second would never start (#57). |
I can continue discussing this on #57 but needed clarity on 2 things:
What if we could have both. Those tests that don't need concurrent fixtures (because of interdependencies) pass in a
|
Looked at this again and it wasn't as tricky as I thought. (At least, I think it's not! We'll see if anyone pokes any holes in it :-).) Anyway: #121 |
Neither was I. I will try harder over the next few weeks though! |
There's however something, that I think is being discussed at #110 This is my own take on it. Given a test file test_x.py: # -*- coding: utf-8 -*-
import trio
async def test_passes_shorter(boom):
await trio.sleep(boom - 2) # will pass as the nursery is cancelled before the exception is thrown and a conftest.py: # -*- coding: utf-8 -*-
from pytest_trio.enable_trio_mode import *
from functools import partial
import trio
import pytest
from loguru import logger
BLOCKING_PERIOD = 5
async def run_boom(task_status=trio.TASK_STATUS_IGNORED):
logger.error("run_boom:start")
task_status.started(BLOCKING_PERIOD)
await trio.sleep(BLOCKING_PERIOD)
logger.error("run_boom:hitting-Exception")
raise Exception() # This gets thrown as expected IFF this is still running (which is when the tests aren't done yet/still running themselves)
@pytest.fixture
async def boom(nursery):
logger.debug("boom:start")
time_start = trio.current_time()
blocking_period = await nursery.start(partial(run_boom))
yield blocking_period
time_end = trio.current_time()
logger.debug(f"boom:blocking_period: {blocking_period} -> {time_end - time_start}")
logger.debug("boom:end") The test will always pass. |
Given a test file
test_x.py
:and a
conftest.py
:The test suite never ends because
booms_ready.set()
is never reached. As a result,booms_ready.wait()
blocks.One way around this could be to detect that
run_boom
raised an Exception and then send a cancel scope tonursery
?If that's a solution, how do I go about it?
The text was updated successfully, but these errors were encountered: