-
Notifications
You must be signed in to change notification settings - Fork 41
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
Fail test if there are unhandled exceptions or pending tasks. #60
base: master
Are you sure you want to change the base?
Conversation
Hi, this is a nice feature. I think that the best way to integrate it in asynctest is to use it through the @fail_on decorator: That would allow users to deactivate this check when they need to: for instance, when a user overrides the default exception handler to centralize the way common exceptions are handled in its asyncio app. If you want to update your PR and need some help doing so, don't hesitate to ask for help. I added some documentation in the _fail_on module. |
@Martiusweb For this feature it's important to call The |
My experience with First of all, I think this would be quite unexpected by a lot of users. Not to mention the potential effect of repeatedly calling this function on performance. Second of all, relying on Maybe there's a way to track the futures and tasks coming out of the event loop and check them at the end of the test? Subclassing? Just thinking "out loud" really. My 2¢ Rémi |
I cannot imagine a test that would suffer more than a couple ms from gc.collect, but that can be a made a separate configuration option.
But I believe that it should be on by default, because you do want errors like this to be fixed asap. And due to their unpredictable nature the only way is to “randomly” call gc.collect along the execution.
The only reliable source is to cause Task’s `__del__` to be triggered. I guess if there would be a better way, asyncio would already support it. It worths to take a look at how other major asyncio frameworks (e.g. aiohttp) test against that though.
There was a thread on python ideas not long ago about adding gc.collect to unittest. I’ll attach a link if I find it.
Best Regards
Ilya Kulakov
… On Dec 8, 2017, at 5:37 AM, Rémi Cardona ***@***.***> wrote:
My experience with gc tells me that adding a call to collect() is a bad idea.
First of all, I think this would be quite unexpected by a lot of users. Not to mention the potential effect of repeatedly calling this function on performance.
Second of all, relying on collect() feels inherently wrong to me, as the GC is an implementation detail of CPython.
Maybe there's a way to track the futures and tasks coming out of the event loop and check them at the end of the test? Subclassing? Just thinking "out loud" really.
My 2¢
Rémi
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.
|
On the second thought, it can be removed and users can run it manually on demand. What has to be fixed is an ability to run checks after the loop is closed and destroyed. |
Hi, I'm quite busy this month, and I can't find enough time to correctly review the opened PRs, thus, sorry for the delays. If you think that we should rework @fail_on to be able to perform some checks at a different time, I'm OK with this. If you need to ensure that the loop has been destroyed, I think it's doable with a finalizer (https://docs.python.org/3/library/weakref.html#weakref.finalize). However, the checks must always be triggered inside the test case, which means that we probably need to ensure that there are no other references to the loop in user's code... which is a pretty big assumption. On the other hand, I'm not sure that we really need this (ensure that the loop is destroyed). |
@Martiusweb Destruction is necessary to trigger the check. We could issue a warning if loop is still alive by the end of the test. |
@Martiusweb Any thoughts regarding that improvement? |
I really think this should be implemented with
I also have concerns with the use of gc.collect(): how does collecting a pending task raises an exception? I don't think we should rely on this. Maybe this PR should be split in two:
|
These are details of CPython's asyncio implementation: The error is only triggered upon object's destruction with a pending exception. Alternatively one could go over all the tasks and check for the |
IMHO it's a bug in asyncio which should not keep live frames. But nobody
came with a fix for this.
There is now traceback.clear_frames().
|
I'd just iterate over all tasks using |
Not sure I understood @vstinner comment. We are trying to come up with a solution to check if there are tasks with pending exceptions by the time the test is done. One way is to rely on CPython’s implementation that calls loop’s unhandeled exception handler upon Task’s deletion if it has a pending exception. |
@1st1 We do not control the flow within a test case. Tasks with unhandled exceptions can be deleted before the execution will reach our code. These deleted tasks won't appear in |
Another idea would be to catch all warnings, match them, and turn them into an error if they're coming from an asyncio task. |
But again, warnings are issued upon object's destruction which may or may not happen and therefore usage of |
Well, yes, set up a custom warnings filter, and run |
@1st1 Pushed one more attempt of an implementation that avoids One issue though is that context passed to custom exception handler does not receive |
When set, tests will fail if there are unhandled exceptions.
I don't understand what can be captured via warnings. The first solution we're exploring is relying on a custom exception handler in the loop and relying on gc.collect() to ensure the exception handler is called. I'm concerned about the portability and sustainability of this solution because I don't know if we can rely on gc.collect() on other implementations of python (in particular pypy). I also don't know if we should assume this behavior in other implementations of asyncio (like uvloop). In fact, asynctest already assumes a lot about internals of the implementation of the loop, which is something I'd like to take care of (one day...). Alternatively, we can create a wrapper around the task to track pending exceptions. Asynctest would hook itself at a higher level, which seems safer. The main downside I see is that it's only available since 3.4.4 (we can test if the feature is available and raise a warning if not). |
c2e9ce1
to
967481c
Compare
This is a backport from @GreatFruitOmsk's internal async_unittest written by @amezin.