Skip to content

Commit 104b612

Browse files
authored
imprv: generate async overloads for finish-func methods (#307)
- Detect callable methods that have a paired `*_finish` function and emit three `@overload` variants for each: an awaitable form returning `_gi.Async[...]`, a keyword-only callback form, and a positional callback form with `*user_data`. - Add `Arguments.as_async` / `as_async_callback_kwargs` to derive the visible argument set for each overload, and synthesize a `Gio.AsyncReadyCallback` type alias parameterized by the source object type. - Add helpers to the `utils.py` module: a `MISSING` sentinel, `get_finish_func`, and a `cache_slot` decorator that lazily populates dataclass slots (works for frozen dataclasses via `object.__setattr__`). - Refactor `class_info.py` and `stub.py` to use these helpers, and extend `get_namespace_member` to accept a `BaseInfo`. Fixes #220 Fixes #302
1 parent 093ce1a commit 104b612

12 files changed

Lines changed: 3411 additions & 607 deletions

File tree

src/gi-stubs/_gi.pyi

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
from typing import Any
22
from typing import ClassVar
33
from typing import Final
4+
from typing import final
5+
from typing import Generic
46
from typing import Protocol
57
from typing import type_check_only
68
from typing_extensions import Self
9+
from typing_extensions import TypeVar
710

811
from builtins import Warning as _Warning
912
from collections.abc import Callable
13+
from collections.abc import Generator
1014
from collections.abc import Mapping
1115
from collections.abc import Sequence
1216
from contextlib import AbstractContextManager
17+
from contextvars import Context
1318
from enum import IntEnum
1419
from enum import IntFlag
1520
from inspect import Signature
@@ -18,6 +23,8 @@ from gi.repository import Gio
1823
from gi.repository import GLib
1924
from gi.repository import GObject as _GObject
2025

26+
_T_co = TypeVar("_T_co", covariant=True, default=Any)
27+
2128
G_MAXDOUBLE: Final[float]
2229
G_MAXFLOAT: Final[float]
2330
G_MAXINT: Final[int]
@@ -60,23 +67,24 @@ class ArrayType(int):
6067
C: ClassVar[int]
6168
PTR_ARRAY: ClassVar[int]
6269

63-
class Async:
70+
@final
71+
class Async(Generic[_T_co]):
6472
cancellable: Gio.Cancellable | None
6573
def __init__(
6674
self, finish_func: CallableInfo, cancellable: Gio.Cancellable | None = None
6775
) -> None: ...
6876
def add_done_callback(
69-
self, callback: Callable[..., Any], *, context: object = None
77+
self, callback: Callable[[Self], object], *, context: Context | None = None
7078
) -> None: ...
71-
def cancel(self) -> None: ...
79+
def cancel(self, msg: str | None = None) -> None: ...
7280
def done(self) -> bool: ...
7381
def exception(self) -> BaseException | None: ...
74-
def remove_done_callback(self, callback: Callable[..., Any], /) -> int: ...
75-
def result(self) -> Any: ...
76-
def __await__(self) -> Async: ...
82+
def remove_done_callback(self, callback: Callable[[Self], object], /) -> int: ...
83+
def result(self) -> _T_co: ...
84+
def __await__(self) -> Generator[Self, Any, _T_co]: ...
7785
def __del__(self) -> None: ...
78-
def __iter__(self) -> Async: ...
79-
def __next__(self) -> Async: ...
86+
def __iter__(self) -> Self: ...
87+
def __next__(self) -> Self: ...
8088

8189
class BaseInfo:
8290
def equal(self, object: BaseInfo, /) -> bool: ...
@@ -333,6 +341,7 @@ class ObjectProtocol(GObjectProtocol, Protocol):
333341

334342
class GObject(GObjectProtocol):
335343
__gtype__: ClassVar[GType]
344+
@type_check_only
336345
class Props: ...
337346

338347
@property

src/gi-stubs/repository/Adw.pyi

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any
22
from typing import Final
33
from typing import Literal
4+
from typing import overload
45
from typing import Protocol
56
from typing import type_check_only
67
from typing import TypeAlias
@@ -1031,16 +1032,28 @@ class AlertDialog(Dialog):
10311032
width_request: int = ...,
10321033
) -> None: ...
10331034
def add_response(self, id: str, label: str) -> None: ...
1035+
@overload
10341036
def choose(
10351037
self,
10361038
parent: _Gtk4.Widget | None = None,
10371039
cancellable: Gio.Cancellable | None = None,
1038-
callback: Callable[
1039-
[GObject.Object | None, Gio.AsyncResult, Unpack[_DataTs]], None
1040-
]
1041-
| None = None,
1040+
) -> _gi.Async[str]: ...
1041+
@overload
1042+
def choose(
1043+
self,
1044+
parent: _Gtk4.Widget | None,
1045+
cancellable: Gio.Cancellable | None,
1046+
callback: Gio.AsyncReadyCallback[AlertDialog, Unpack[_DataTs]] | None,
10421047
*user_data: Unpack[_DataTs],
10431048
) -> None: ...
1049+
@overload
1050+
def choose(
1051+
self,
1052+
parent: _Gtk4.Widget | None = None,
1053+
cancellable: Gio.Cancellable | None = None,
1054+
*,
1055+
callback: Gio.AsyncReadyCallback[AlertDialog] | None,
1056+
) -> None: ...
10441057
def choose_finish(self, result: Gio.AsyncResult) -> str: ...
10451058
def do_response(self, response: str) -> None: ...
10461059
def get_body(self) -> str: ...
@@ -5706,15 +5719,22 @@ class MessageDialog(_Gtk4.Window):
57065719
width_request: int = ...,
57075720
) -> None: ...
57085721
def add_response(self, id: str, label: str) -> None: ...
5722+
@overload
5723+
def choose(self, cancellable: Gio.Cancellable | None = None) -> _gi.Async[str]: ...
5724+
@overload
57095725
def choose(
57105726
self,
5711-
cancellable: Gio.Cancellable | None = None,
5712-
callback: Callable[
5713-
[GObject.Object | None, Gio.AsyncResult, Unpack[_DataTs]], None
5714-
]
5715-
| None = None,
5727+
cancellable: Gio.Cancellable | None,
5728+
callback: Gio.AsyncReadyCallback[MessageDialog, Unpack[_DataTs]] | None,
57165729
*user_data: Unpack[_DataTs],
57175730
) -> None: ...
5731+
@overload
5732+
def choose(
5733+
self,
5734+
cancellable: Gio.Cancellable | None = None,
5735+
*,
5736+
callback: Gio.AsyncReadyCallback[MessageDialog] | None,
5737+
) -> None: ...
57185738
def choose_finish(self, result: Gio.AsyncResult) -> str: ...
57195739
def do_response(self, response: str) -> None: ...
57205740
def get_body(self) -> str: ...

src/gi-stubs/repository/GdkPixbuf.pyi

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any
22
from typing import Final
33
from typing import Literal
4+
from typing import overload
45
from typing import type_check_only
56
from typing import TypeAlias
67
from typing_extensions import TypeVarTuple
@@ -181,16 +182,27 @@ class Pixbuf(GObject.Object, Gio.Icon, Gio.LoadableIcon):
181182
def get_colorspace(self) -> Colorspace: ...
182183
@staticmethod
183184
def get_file_info(filename: str) -> tuple[PixbufFormat | None, int, int]: ...
185+
@overload
186+
@staticmethod
187+
def get_file_info_async(
188+
filename: str, cancellable: Gio.Cancellable | None = None
189+
) -> _gi.Async[tuple[PixbufFormat | None, int, int]]: ...
190+
@overload
184191
@staticmethod
185192
def get_file_info_async(
186193
filename: str,
187-
cancellable: Gio.Cancellable | None = None,
188-
callback: Callable[
189-
[GObject.Object | None, Gio.AsyncResult, Unpack[_DataTs]], None
190-
]
191-
| None = None,
194+
cancellable: Gio.Cancellable | None,
195+
callback: Gio.AsyncReadyCallback[None, Unpack[_DataTs]] | None,
192196
*user_data: Unpack[_DataTs],
193197
) -> None: ...
198+
@overload
199+
@staticmethod
200+
def get_file_info_async(
201+
filename: str,
202+
cancellable: Gio.Cancellable | None = None,
203+
*,
204+
callback: Gio.AsyncReadyCallback[None] | None,
205+
) -> None: ...
194206
@staticmethod
195207
def get_file_info_finish(
196208
async_result: Gio.AsyncResult,
@@ -263,16 +275,27 @@ class Pixbuf(GObject.Object, Gio.Icon, Gio.LoadableIcon):
263275
def new_from_stream(
264276
cls, stream: Gio.InputStream, cancellable: Gio.Cancellable | None = None
265277
) -> Pixbuf | None: ...
278+
@overload
279+
@staticmethod
280+
def new_from_stream_async(
281+
stream: Gio.InputStream, cancellable: Gio.Cancellable | None = None
282+
) -> _gi.Async[Pixbuf | None]: ...
283+
@overload
266284
@staticmethod
267285
def new_from_stream_async(
268286
stream: Gio.InputStream,
269-
cancellable: Gio.Cancellable | None = None,
270-
callback: Callable[
271-
[GObject.Object | None, Gio.AsyncResult, Unpack[_DataTs]], None
272-
]
273-
| None = None,
287+
cancellable: Gio.Cancellable | None,
288+
callback: Gio.AsyncReadyCallback[None, Unpack[_DataTs]] | None,
274289
*user_data: Unpack[_DataTs],
275290
) -> None: ...
291+
@overload
292+
@staticmethod
293+
def new_from_stream_async(
294+
stream: Gio.InputStream,
295+
cancellable: Gio.Cancellable | None = None,
296+
*,
297+
callback: Gio.AsyncReadyCallback[None] | None,
298+
) -> None: ...
276299
@classmethod
277300
def new_from_stream_at_scale(
278301
cls,
@@ -409,16 +432,27 @@ class PixbufAnimation(GObject.Object):
409432
def new_from_stream(
410433
cls, stream: Gio.InputStream, cancellable: Gio.Cancellable | None = None
411434
) -> PixbufAnimation | None: ...
435+
@overload
436+
@staticmethod
437+
def new_from_stream_async(
438+
stream: Gio.InputStream, cancellable: Gio.Cancellable | None = None
439+
) -> _gi.Async[PixbufAnimation | None]: ...
440+
@overload
412441
@staticmethod
413442
def new_from_stream_async(
414443
stream: Gio.InputStream,
415-
cancellable: Gio.Cancellable | None = None,
416-
callback: Callable[
417-
[GObject.Object | None, Gio.AsyncResult, Unpack[_DataTs]], None
418-
]
419-
| None = None,
444+
cancellable: Gio.Cancellable | None,
445+
callback: Gio.AsyncReadyCallback[None, Unpack[_DataTs]] | None,
420446
*user_data: Unpack[_DataTs],
421447
) -> None: ...
448+
@overload
449+
@staticmethod
450+
def new_from_stream_async(
451+
stream: Gio.InputStream,
452+
cancellable: Gio.Cancellable | None = None,
453+
*,
454+
callback: Gio.AsyncReadyCallback[None] | None,
455+
) -> None: ...
422456
@classmethod
423457
def new_from_stream_finish(
424458
cls, async_result: Gio.AsyncResult

0 commit comments

Comments
 (0)