-
Notifications
You must be signed in to change notification settings - Fork 150
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
Add cleanup code to event_loop that mimics the behavior of asyncio.run #309
base: main
Are you sure you want to change the base?
Conversation
pytest_asyncio/plugin.py
Outdated
loop.close() | ||
# Cleanup code copied from the implementation of asyncio.run() | ||
try: | ||
asyncio.runners._cancel_all_tasks(loop) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling private functions here is a bit sketch, but it's what I chose to do in my project instead of copy/pasting the entire implementation. Might want to go with the copy/paste approach though for this library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late response… There are a couple of ideas flying around, all of which address this issue in some way. That makes it hard to review your PR, so I put it off for too long.
I agree that calling _cancel_all_tasks
might not be a good idea. Although we test with different CPython versions, _cancel_all_tasks
is an implementation detail that could change in a patch release. Since we only test different minor releases (i.e. 3.7, 3.8, …) a change of _cancel_all_tasks
could slip through unnoticed.
It would be best to replicate the behaviour of _cancel_all_tasks
in pytest-asyncio. We cant't do a 1:1 copy and paste, though, because the CPython code is protected by copyright and under a different license than pytest-asyncio.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still stand by my last comment on the use of _cancel_all_tasks
.
Thanks for the contribution! The linked issue is not the only request of that feature. There is also #222. We closed #222 in favour of #235, which intends to used The aioloop-proxy issue has not been started, as far as I know. I think it's a good idea to add clean up code for outstanding tasks independently of #235. Could you add some tests that assert the correct behaviour of your code? |
Great! I've read through the issues you linked and I think I understand the current state of this feature. Sounds like eventually a more sophisticated solution will be added, but for now this PR will be useful. I'll get the development environment set up and work on writing a few tests! |
Thanks for uploading the latest version. I had some time to look into the issue that causes The culprit seems to be I was trying to work around the issue by using |
…effects on other tests. Signed-off-by: Michael Seifert <[email protected]>
…ignature. This prevents gc.collect() from being unreachable. Signed-off-by: Michael Seifert <[email protected]>
…l_tasks. Signed-off-by: Michael Seifert <[email protected]>
b4f4d1b
to
5024eb8
Compare
Codecov ReportBase: 93.81% // Head: 93.23% // Decreases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## master #309 +/- ##
==========================================
- Coverage 93.81% 93.23% -0.58%
==========================================
Files 3 3
Lines 275 281 +6
Branches 41 43 +2
==========================================
+ Hits 258 262 +4
- Misses 11 12 +1
- Partials 6 7 +1
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
I modified |
# as possible (these are triggered in various __del__ methods). | ||
# Without this, resources opened in one test can fail other tests | ||
# when the warning is generated. | ||
gc.collect() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bunch of people implement their own event_loop
fixture, in order to control fixture scope. If we change the fixture definition, we'd have to inform them to adjust as well.
Asking users to remove loop.close()
to benefit from the new cleanup code is an easy sell. Adding a call to gc.collect
in the event_loop fixture not so much.
Is there away around the gc.collect
call in the fixture code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose the call to gc.collect
isn't directly related to task cleanup. I added this in my codebase because there were some tests that opened sockets and never closed them. Having the garbage collector run during the test teardown would make sure that the correct test failed when running pytest with -W error
. But this is unrelated to pytest-asyncio so maybe it doesn't belong here.
I've run into the issue of tasks leaking over test bounderings when i tried to get home assistant updated to later pytest-asyncio: So far i have these fixes/hacks: I've not managed to get it to run reliable as of yet thou. Cleaning up of lingering tasks: Cleaning up of lingering timers: |
I also just noticed that aiohttp loop fixture did trigger a GC call after test finishes: https://github.com/aio-libs/aiohttp/blob/905e0625777c20e4cf2d57fcfd4355827312f797/aiohttp/test_utils.py#L525 That likely explains some of my failures. |
Some test will trigger warnings on garbage collect, these warnings spills over into next test. Some test trigger tasks that raise errors on shutdown, these spill over into next test. This is to mimic older pytest-aiohttp and it's behaviour on test cleanup. Discussions on similar changes for pytest-aiohttp are here: pytest-dev/pytest-asyncio#309
Some test will trigger warnings on garbage collect, these warnings spills over into next test. Some test trigger tasks that raise errors on shutdown, these spill over into next test. This is to mimic older pytest-aiohttp and it's behaviour on test cleanup. Discussions on similar changes for pytest-aiohttp are here: pytest-dev/pytest-asyncio#309
* Upgrade pytest-aiohttp * Make sure executors, tasks and timers are closed Some test will trigger warnings on garbage collect, these warnings spills over into next test. Some test trigger tasks that raise errors on shutdown, these spill over into next test. This is to mimic older pytest-aiohttp and it's behaviour on test cleanup. Discussions on similar changes for pytest-aiohttp are here: pytest-dev/pytest-asyncio#309 * Replace loop with event_loop * Make sure time is frozen for tests * Make sure the ConditionType is not async /home-assistant/homeassistant/helpers/template.py:2082: RuntimeWarning: coroutine 'AsyncMockMixin._execute_mock_call' was never awaited def wrapper(*args, **kwargs): Enable tracemalloc to get traceback where the object was allocated. See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info. * Increase litejet press tests with a factor 10 The times are simulated anyway, and we can't stop the normal event from occuring. * Use async handlers for aiohttp tests/components/motioneye/test_camera.py::test_get_still_image_from_camera tests/components/motioneye/test_camera.py::test_get_still_image_from_camera tests/components/motioneye/test_camera.py::test_get_stream_from_camera tests/components/motioneye/test_camera.py::test_get_stream_from_camera tests/components/motioneye/test_camera.py::test_camera_option_stream_url_template tests/components/motioneye/test_camera.py::test_camera_option_stream_url_template /Users/joakim/src/hass/home-assistant/venv/lib/python3.9/site-packages/aiohttp/web_urldispatcher.py:189: DeprecationWarning: Bare functions are deprecated, use async ones warnings.warn( * Switch to freezegun in modbus tests The tests allowed clock to tick in between steps * Make sure skybell object are fully mocked Old tests would trigger attempts to post to could services: ``` DEBUG:aioskybell:HTTP post https://cloud.myskybell.com/api/v3/login/ Request with headers: {'content-type': 'application/json', 'accept': '*/*', 'x-skybell-app-id': 'd2b542c7-a7e4-4e1e-b77d-2b76911c7c46', 'x-skybell-client-id': '1f36a3c0-6dee-4997-a6db-4e1c67338e57'} ``` * Fix sorting that broke after rebase
* Upgrade pytest-aiohttp * Make sure executors, tasks and timers are closed Some test will trigger warnings on garbage collect, these warnings spills over into next test. Some test trigger tasks that raise errors on shutdown, these spill over into next test. This is to mimic older pytest-aiohttp and it's behaviour on test cleanup. Discussions on similar changes for pytest-aiohttp are here: pytest-dev/pytest-asyncio#309 * Replace loop with event_loop * Make sure time is frozen for tests * Make sure the ConditionType is not async /home-assistant/homeassistant/helpers/template.py:2082: RuntimeWarning: coroutine 'AsyncMockMixin._execute_mock_call' was never awaited def wrapper(*args, **kwargs): Enable tracemalloc to get traceback where the object was allocated. See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info. * Increase litejet press tests with a factor 10 The times are simulated anyway, and we can't stop the normal event from occuring. * Use async handlers for aiohttp tests/components/motioneye/test_camera.py::test_get_still_image_from_camera tests/components/motioneye/test_camera.py::test_get_still_image_from_camera tests/components/motioneye/test_camera.py::test_get_stream_from_camera tests/components/motioneye/test_camera.py::test_get_stream_from_camera tests/components/motioneye/test_camera.py::test_camera_option_stream_url_template tests/components/motioneye/test_camera.py::test_camera_option_stream_url_template /Users/joakim/src/hass/home-assistant/venv/lib/python3.9/site-packages/aiohttp/web_urldispatcher.py:189: DeprecationWarning: Bare functions are deprecated, use async ones warnings.warn( * Switch to freezegun in modbus tests The tests allowed clock to tick in between steps * Make sure skybell object are fully mocked Old tests would trigger attempts to post to could services: ``` DEBUG:aioskybell:HTTP post https://cloud.myskybell.com/api/v3/login/ Request with headers: {'content-type': 'application/json', 'accept': '*/*', 'x-skybell-app-id': 'd2b542c7-a7e4-4e1e-b77d-2b76911c7c46', 'x-skybell-client-id': '1f36a3c0-6dee-4997-a6db-4e1c67338e57'} ``` * Fix sorting that broke after rebase
So are the open threads the only things left for this to go forward? Or is there something else missing? I'll try to find some time for this. |
This is something I found useful and implemented in one of my own projects so I thought I'd make a PR in case the maintainers think this would be a good addition to the library itself. I'd be happy to modify it if only a subset of the changes is desired.
Some issues that I ran into with the current implementation:
loop.close()
is not sufficient to shutdown the event loop. This can make it extremely difficult to track down which test is causing the errors, since the test that fails is not actually the one that has the problem.-W error
under python 3.9 a similar problem happens with ResourceWarnings, where it becomes extremely difficult to figure out which test is actually opening the resources that are generating the warnings since the GC doesn't actually clean them up until some later test is already running.Closes #200