beeware / beeware/toga

Audit exception handling behavior (esp RuntimeError)

Open
#2,806 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
5.4k
Forks
827
Avg merge
9h 45m
Merged PRs (30d)
58

Description

There are potentially some inconsistencies in exception handling behavior, especially as a result of WebView handling, and the handling of the Winforms asyncio event loop integration. We need to audit these interactions to ensure consistency across platforms.

_Originally posted by @rmartin16 in https://github.com/beeware/toga/issues/2781#issuecomment-2323553447_

> > > My original thought was to go non-async, with `app.loop.call_soon_threadsafe()`... not sure if that will change anything by ensuring that you're getting a method on the app's specific loop, rather than potentially spawning a thread-specific loop in the .NET context.
> >
> >
> > This did print an exception to the console; however, the app continued running.
>
> That's not _entirely_ unexpected. The Winforms asyncio integration is a bit weird, because of the winforms STA apartment state. The app itself runs in a thread; exceptions are caught and the stack trace is manually unwound for printing purposes, but won't necessarily terminate the app itself.
>
> I'm not 100% convinced this implementation is correct - the intention was that an exception in an event handler would be reported, but wouldn't kill the app; but it's possible this is being a little _too_ enthusiastic with surpassing exit conditions, and actual errors in the app mainline aren't being surfaced.

So, my interest remain piqued since you said this.

To recap, I had put a task on the event loop that would raise a `RuntimeError`....and all that happened was the stacktrace was printed to the console.

I tracked down this behavior to `BaseEventLoop.default_exception_handler()`. If I add a `default_exception_handler()` to `WinformsProactorEventLoop`, I see it run for the `RuntimeError`.
```python
def default_exception_handler(self, context):
print("this is your friendly exception handler")
super().default_exception_handler(context)
```

```
[webview] Starting in dev mode...
===========================================================================
this is your friendly exception handler
Exception in callback WebView.winforms_initialization_completed..raise_exception() at C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\widgets\webview.py:72
handle: .raise_exception() at C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\widgets\webview.py:72 created at C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\widgets\webview.py:75>
source_traceback: Object created at (most recent call last):
File "C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\app.py", line 167, in _run_app
self.loop.run_forever(self)
File "C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\libs\proactor.py", line 76, in run_forever
WinForms.Application.Run(self.app.app_context)
File "C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\libs\wrapper.py", line 22, in __call__
return function(*args, **kwargs)
File "C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\widgets\webview.py", line 75, in winforms_initialization_completed
self.interface.app.loop.call_soon_threadsafe(raise_exception)
Traceback (most recent call last):
File "C:\Users\user\.pyenv\pyenv-win\versions\3.12.1\Lib\asyncio\events.py", line 84, in _run
self._context.run(self._callback, *self._args)
File "C:\Users\user\github\beeware\toga\winforms\src\toga_winforms\widgets\webview.py", line 73, in raise_exception
raise RuntimeError("failed to load WebView")
RuntimeError: failed to load WebView
```
**So, given this, what is the desired behavior when Toga itself raises an exception?**

If I simulate raising the `RuntimeError` for WebView on Gtk, a console error is logged but the app window never renders.

```
[webview] Starting in dev mode...
===========================================================================
Traceback (most recent call last):
File "/home/russell/github/beeware/toga/gtk/src/toga_gtk/app.py", line 45, in gtk_startup
self.interface._startup()
File "/home/russell/.pyenv/versions/briefcase-3.10/lib/python3.10/site-packages/toga/app.py", line 619, in _startup
self.startup()
File "/home/russell/github/beeware/toga/examples/webview/webview/app.py", line 86, in startup
self.webview = toga.WebView(
File "/home/russell/.pyenv/versions/briefcase-3.10/lib/python3.10/site-packages/toga/widgets/webview.py", line 47, in __init__
self._impl = self.factory.WebView(interface=self)
File "/home/russell/github/beeware/toga/gtk/src/toga_gtk/widgets/base.py", line 16, in __init__
self.create()
File "/home/russell/github/beeware/toga/gtk/src/toga_gtk/widgets/webview.py", line 15, in create
raise RuntimeError(
RuntimeError: Unable to import WebKit2. Ensure that the system package providing WebKit2 and its GTK bindings have been installed. See https://toga.readthedocs.io/en/stable/reference/api/widgets/webview.html#system-requirements for details.
```

Contributor guide

Open the contributing guide

Research direction

Start with the WinForms paths in winforms/src/toga_winforms/widgets/webview.py, winforms/src/toga_winforms/libs/proactor.py, and winforms/src/toga_winforms/app.py, then compare them with gtk/src/toga_gtk/app.py and the asyncio BaseEventLoop.default_exception_handler() behavior. Reproduce the WebView RuntimeError on WinForms and GTK, determine the intended handling for event-handler and app-mainline exceptions, and verify that behavior is consistent across platforms.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.