Type async method return values correctly
With the new asyncio support in PyGObject, it'd be nice if async methods would type their return values as awaitables: Awaitable[Gio.File]for Gtk.FileDialog.open() for example.
Is adding Awaitable[x] as return type enough for all cases?
After a quick search it seems that all callbacks, cancellables and user_data parameters are optionals, so we don't really need to remove them from the function signature. There are some edge cases?
Anyway to extract the correct return type to insert in Awaitable I think we need this changes to reach some stable version: https://gitlab.gnome.org/GNOME/pygobject/-/merge_requests/428
I tried with this little example:
import gi
from gi.repository import Gtk
async def fun():
await Gtk.FileDialog().open()
And I changed the stubs like this
def open(
self,
parent: typing.Optional[Window] = None,
cancellable: typing.Optional[Gio.Cancellable] = None,
callback: typing.Optional[typing.Callable[..., None]] = None,
*user_data: typing.Any,
) -> None | typing.Awaitable[Gio.File]: ...
And pyright complains with this error:
/home/matteo/tmp/test.py:5:11 - error: "None" is not awaitable
"None" is incompatible with protocol "Awaitable[_T_co@Awaitable]"
"__await__" is not present (reportGeneralTypeIssues)
1 error, 1 warning, 0 informations
The only way to fix this is to remove the None type as return, but then we lose the non async version of the function.
mypy gives no error ever, probably it doesn't check for the return to be Awaitable.
I am not sure we can actually support async functions.
Is adding Awaitable[x] as return type enough for all cases?
When the callback argument is provided, the method will return None, which is basically saying "I will handle the result in the callback, I don't care about the return value here". So just setting the return value to Awaitable[x] seems fine, since that'll be returned when you actually care about the return value.
Otherwise, things will get more complicated:
- I don't think there's a way to have conditional typing
- Generating separate stubs for async seems overkill
So just setting the return value to
Awaitable[x]seems fine, since that'll be returned when you actually care about the return value.
Well, this is seems like an hack, but it might work.
Let's wait for the next pygobject release, then we can extract the finish function to understand if a function is awaitable and the actual return type.