-
-
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
nursery fixture is too error-prone #110
Comments
Some updates were made recently that now pass the cancel scope properly. This will allow your fixtures or dependent functions to detect and handle the cancel scope. FYI: #120 (comment) Can you revisit? Until it's merged in, it's: #121 |
I think the way forward here is probably to:
|
I strongly agree about removing the nursery fixture altogether, see: Asynchronous yield fixture is much better (and allow user to implement it own nursery fixture if he really want to shoot hemself in the foot ) |
I think people would appreciate a short example that shows why/when the nursery fixture is a bad idea and how to fix/address it (possibly using asynchronous yield fixtures) If this requires a lot of code, can you explain some issues the current issues the nursery fixture can lead to? I might be able to cook up an example but I am atleast a month away and would like to see feedback from others as well so I don't feel alone ! 🎉 |
I'm not sure this addresses the point. It may not have been the intended use, but I suspect people mostly use the
async test_foo(nursery):
connection = bar()
@nursery.start_soon
async _listener():
assert await connection.receive() == 'baz'
await connection.send('not-baz')
# Whoops-- exiting test body will cancel the `_listener` task before it
# evaluates its `assert`. Most pytest-trio users have no idea that `nursery`
# fixture behaves this way, and their test will incorrectly pass. corrected usage 1: explicitly open_nursery() instead of using corrected usage 2: use the fixture, but wait for important tasks to complete before exiting the test async test_foo(nursery):
done = trio.Event()
connection = bar()
@nursery.start_soon
async _listener():
assert await connection.receive() == 'baz'
done.set()
await connection.send('not-baz')
await done.wait() Per my original report:
async test_foo(simple_nursery):
connection = bar()
@simple_nursery.start_soon
async _listener():
assert await connection.receive() == 'baz'
await connection.send('not-baz')
# OK: test will block on completion of `_listener` task |
So this I mean, we could setup a Why won't I make The @pytest.fixture
async done():
return trio.Event()
@pytest.fixture
async _listener(done)
# something with await connection.receive() …
done.set()
async test_foo(nursery, _listener, done):
connection = bar()
await connection.send('not-baz')
await done.wait()
assert _listener.value == 'baz' |
The goal is not to refactor my simplistic test example-- rather to avoid surprises commonly encountered by pytest-trio users. (E.g. let's not assume the user is comfortable with writing fixtures.) I don't think @pytest.fixture
async simple_nursery()
async with trio.open_nursery() as nursery:
yield nursery |
Even though I'm well aware of the
nursery
fixture's behavior of cancelling the nursery when the test function reaches end of scope, too many times I've written invalid tests that silently end without completing the test code. This is the worst kind of trap for test authors.In hindsight, it would have been better for the
nursery
fixture to not automatically cancel. The test writer would have to cancel explicitly, and the utility of the fixture is simply to avoid boilerplate and extra nesting. The big advantage is that the failure mode when the test author does the wrong thing is obvious: the test will hang.At this point, changing the
nursery
semantics would break the world and cause confusion. So perhaps the best we can do is add a fixture under another name, likesimple_nursery
.The text was updated successfully, but these errors were encountered: