Skip to content

Commit b2dd5bd

Browse files
committed
new: Migrate stubs generator to GIRepository 3.0
https://docs.gtk.org/girepository/migrating-gi.html This requires pygobject > 3.50 to be installed
1 parent a1551ae commit b2dd5bd

File tree

2 files changed

+78
-58
lines changed

2 files changed

+78
-58
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ build
66
dist
77
*.egg-info
88
*.sublime-*
9+
*.vscode
910
*.venv
1011

1112
src/gi-stubs/repository/Gdk.pyi

tools/generate.py

Lines changed: 77 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import gi._gi as GI
2727
import parse
2828

29-
gi.require_version("GIRepository", "2.0")
29+
gi.require_version("GIRepository", "3.0")
3030
from gi.repository import GIRepository
3131
from gi.repository import GObject
3232

@@ -44,6 +44,7 @@ def fix_argument_name(name: str) -> str:
4444

4545

4646
def _object_get_props(
47+
repo: GIRepository.Repository,
4748
obj: GI.ObjectInfo,
4849
) -> Tuple[list[GIRepository.BaseInfo], list[GIRepository.BaseInfo]]:
4950
parents: list[GI.ObjectInfo] = []
@@ -62,35 +63,35 @@ def _object_get_props(
6263

6364
readable_props: list[GIRepository.BaseInfo] = []
6465
writable_props: list[GIRepository.BaseInfo] = []
66+
6567
for prop in props:
66-
repo = GIRepository.Repository.get_default()
6768
namespace = prop.get_namespace()
6869
container = prop.get_container()
6970
class_info = repo.find_by_name(namespace, container.get_name())
7071
if class_info is None:
7172
raise Exception(f"Unable to find {namespace}.{container}")
7273

73-
if class_info.get_type() == GIRepository.InfoType.OBJECT:
74-
n_props = GIRepository.object_info_get_n_properties(class_info)
74+
if isinstance(class_info, GIRepository.ObjectInfo):
75+
n_props = GIRepository.ObjectInfo.get_n_properties(class_info)
7576
for i in range(n_props):
76-
p: GIRepository.BaseInfo = GIRepository.object_info_get_property(
77+
p: GIRepository.BaseInfo = GIRepository.ObjectInfo.get_property(
7778
class_info, i
7879
)
7980
if p.get_name() == prop.get_name():
80-
flags = GIRepository.property_info_get_flags(p)
81+
flags = GIRepository.PropertyInfo.get_flags(p)
8182
if flags & GObject.ParamFlags.READABLE:
8283
readable_props.append(p)
8384
if flags & GObject.ParamFlags.WRITABLE:
8485
writable_props.append(p)
8586

86-
if class_info.get_type() == GIRepository.InfoType.INTERFACE:
87-
n_props = GIRepository.interface_info_get_n_properties(class_info)
87+
if isinstance(class_info, GIRepository.InterfaceInfo):
88+
n_props = GIRepository.InterfaceInfo.get_n_properties(class_info)
8889
for i in range(n_props):
89-
p: GIRepository.BaseInfo = GIRepository.interface_info_get_property(
90+
p: GIRepository.BaseInfo = GIRepository.InterfaceInfo.get_property(
9091
class_info, i
9192
)
9293
if p.get_name() == prop.get_name():
93-
flags = GIRepository.property_info_get_flags(p)
94+
flags = GIRepository.PropertyInfo.get_flags(p)
9495
if flags & GObject.ParamFlags.READABLE:
9596
readable_props.append(p)
9697
if flags & GObject.ParamFlags.WRITABLE:
@@ -116,22 +117,22 @@ def _callable_get_arguments(
116117

117118
# Filter out array length arguments for return type
118119
ret_type = type.get_return_type()
119-
if ret_type.get_array_length() >= 0:
120-
skip.append(ret_type.get_array_length())
120+
if ret_type.get_array_length_index() >= 0:
121+
skip.append(ret_type.get_array_length_index())
121122

122123
for i, arg in enumerate(function_args):
123124
if i in skip:
124125
continue
125126

126-
if arg.get_closure() >= 0:
127+
if arg.get_closure_index() >= 0:
127128
accept_optional_args = True
128-
optional_args_name = function_args[arg.get_closure()].get_name()
129-
skip.append(arg.get_closure())
130-
skip.append(arg.get_destroy())
129+
optional_args_name = function_args[arg.get_closure_index()].get_name()
130+
skip.append(arg.get_closure_index())
131+
skip.append(arg.get_destroy_index())
131132

132133
# Filter out array length args
133-
arg_type = arg.get_type()
134-
len_arg: int = arg_type.get_array_length()
134+
arg_type = arg.get_type_info()
135+
len_arg: int = arg_type.get_array_length_index()
135136
if len_arg >= 0:
136137
skip.append(len_arg)
137138
if len_arg < i:
@@ -140,11 +141,11 @@ def _callable_get_arguments(
140141
dict_return_args.pop(len_arg, None)
141142

142143
# Need to check because user_data can be the first arg
143-
if arg.get_closure() != i and arg.get_destroy() != i:
144+
if arg.get_closure_index() != i and arg.get_destroy_index() != i:
144145
direction = arg.get_direction()
145146
if direction == GI.Direction.OUT or direction == GI.Direction.INOUT:
146147
t = _type_to_python(
147-
arg.get_type(), current_namespace, needed_namespaces, True
148+
arg.get_type_info(), current_namespace, needed_namespaces, True
148149
)
149150

150151
dict_return_args[i] = t
@@ -156,11 +157,11 @@ def _callable_get_arguments(
156157
args = list(dict_args.values())
157158
for a in reversed(args):
158159
t = _type_to_python(
159-
a.get_type(),
160+
a.get_type_info(),
160161
current_namespace,
161162
needed_namespaces,
162163
False,
163-
a.get_closure() >= 0, # True if function admits variable arguments
164+
a.get_closure_index() >= 0, # True if function admits variable arguments
164165
)
165166

166167
if a.may_be_null() and t != "None":
@@ -228,16 +229,16 @@ def get_name(self) -> str:
228229
def get_namespace(self) -> str:
229230
return self.obj.get_namespace()
230231

231-
def get_type(self) -> GIRepository.InfoType:
232-
return self.obj.get_type()
232+
def get_type_info(self) -> GI.InfoType:
233+
return self.obj.get_type_info()
233234

234235

235236
def _build_type(type: GIRepository.BaseInfo) -> TypeInfo:
236237
return TypeInfo(
237238
type,
238-
GIRepository.type_info_get_tag,
239-
GIRepository.type_info_get_param_type,
240-
GIRepository.type_info_get_interface,
239+
GIRepository.TypeInfo.get_tag,
240+
GIRepository.TypeInfo.get_param_type,
241+
GIRepository.TypeInfo.get_interface,
241242
)
242243

243244

@@ -358,9 +359,14 @@ def _type_to_python(
358359
raise ValueError("TODO")
359360

360361

361-
def _build(parent: ObjectT, namespace: str, overrides: dict[str, str]) -> str:
362-
ns = set()
363-
ret = _gi_build_stub(parent, namespace, dir(parent), ns, overrides, None, "")
362+
def _build(
363+
repo: GIRepository.Repository,
364+
parent: ObjectT,
365+
namespace: str,
366+
overrides: dict[str, str],
367+
) -> str:
368+
ns: set[str] = set()
369+
ret = _gi_build_stub(repo, parent, namespace, dir(parent), ns, overrides, None, "")
364370

365371
typevars: list[str] = [
366372
'T = typing.TypeVar("T")',
@@ -393,6 +399,8 @@ def _build(parent: ObjectT, namespace: str, overrides: dict[str, str]) -> str:
393399
return (
394400
"import typing"
395401
+ "\n\n"
402+
+ "import enum"
403+
+ "\n\n"
396404
+ "\n".join(imports)
397405
+ "\n"
398406
+ "\n".join(typevars)
@@ -423,10 +431,11 @@ def _build_function_info(
423431

424432
# Flags
425433
function_flags = function.get_flags()
426-
if function_flags & GI.FunctionInfoFlags.IS_CONSTRUCTOR:
434+
435+
if function_flags & GIRepository.FunctionInfoFlags.IS_CONSTRUCTOR:
427436
constructor = True
428437

429-
if function_flags & GI.FunctionInfoFlags.IS_METHOD:
438+
if function_flags & GIRepository.FunctionInfoFlags.IS_METHOD:
430439
method = True
431440

432441
if in_class and not method and not constructor:
@@ -553,6 +562,7 @@ def _check_override(prefix: str, name: str, overrides: dict[str, str]) -> Option
553562

554563

555564
def _gi_build_stub(
565+
repo: GIRepository.Repository,
556566
parent: ObjectT,
557567
current_namespace: str,
558568
children: list[str],
@@ -587,16 +597,19 @@ def _gi_build_stub(
587597
continue
588598

589599
if inspect.isclass(obj):
590-
if GObject.GFlags in obj.__mro__:
600+
if GObject.GFlags in obj.__mro__ or str(obj).startswith("<flag"):
591601
flags[name] = obj
592-
elif GObject.GEnum in obj.__mro__:
602+
elif GObject.GEnum in obj.__mro__ or str(obj).startswith("<enum"):
593603
enums[name] = obj
594604
else:
595605
classes[name] = obj
596606
elif inspect.isfunction(obj) or inspect.isbuiltin(obj):
597607
functions[name] = obj
598-
elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj):
608+
elif inspect.ismethoddescriptor(obj):
599609
functions[name] = obj
610+
elif inspect.ismethod(obj):
611+
# bound methods
612+
functions[name] = obj.__func__
600613
elif callable(obj):
601614
# Fall back to a function for anything callable
602615
functions[name] = obj
@@ -629,7 +642,7 @@ def _gi_build_stub(
629642

630643
val = constants[name]
631644

632-
if str(val).startswith(("<flags", "<enum")):
645+
if str(val.__class__).startswith(("<flag", "<enum")):
633646
val = val.real
634647

635648
if isinstance(val, str):
@@ -671,6 +684,7 @@ def _gi_build_stub(
671684
full_name = _generate_full_name(prefix_name, name)
672685

673686
classret = _gi_build_stub(
687+
repo,
674688
obj,
675689
current_namespace,
676690
_find_methods(obj),
@@ -691,7 +705,7 @@ def _gi_build_stub(
691705
if isinstance(object_info, GI.StructInfo):
692706
for f in object_info.get_fields():
693707
t = _type_to_python(
694-
f.get_type(), current_namespace, needed_namespaces, True
708+
f.get_type_info(), current_namespace, needed_namespaces, True
695709
)
696710
n = f.get_name()
697711
if n in dir(obj):
@@ -721,7 +735,7 @@ def _gi_build_stub(
721735

722736
for f in object_info.get_fields():
723737
t = _type_to_python(
724-
f.get_type(), current_namespace, needed_namespaces, True
738+
f.get_type_info(), current_namespace, needed_namespaces, True
725739
)
726740
n = f.get_name()
727741
if n in dir(obj):
@@ -732,7 +746,7 @@ def _gi_build_stub(
732746
fields.append(f"{n}: {t}")
733747

734748
# Properties
735-
(rp, wp) = _object_get_props(object_info)
749+
(rp, wp) = _object_get_props(repo, object_info)
736750
readable_props.extend(rp)
737751
writable_props.extend(wp)
738752

@@ -794,18 +808,18 @@ def _gi_build_stub(
794808
# Avoid duplicates
795809
continue
796810
names.append(n)
797-
type = _build_type(GIRepository.property_info_get_type(p))
811+
type = _build_type(GIRepository.PropertyInfo.get_type_info(p))
798812
t = _type_to_python(type, current_namespace, needed_namespaces, True)
799813

800814
# Check getter/setter
801-
getter = GIRepository.property_info_get_getter(p)
802-
setter = GIRepository.property_info_get_setter(p)
803-
if getter and GIRepository.callable_info_may_return_null(getter):
815+
getter = GIRepository.PropertyInfo.get_getter(p)
816+
setter = GIRepository.PropertyInfo.get_setter(p)
817+
if getter and GIRepository.CallableInfo.may_return_null(getter):
804818
s.append(f"{n}: typing.Optional[{t}]")
805819
elif setter and not getter:
806820
# If is writable only prop check if setter can accept NULL
807-
arg_info = GIRepository.callable_info_get_arg(setter, 0)
808-
if GIRepository.arg_info_may_be_null(arg_info):
821+
arg_info = GIRepository.CallableInfo.get_arg(setter, 0)
822+
if GIRepository.ArgInfo.may_be_null(arg_info):
809823
s.append(f"{n}: typing.Optional[{t}]")
810824
else:
811825
s.append(f"{n}: {t}")
@@ -838,12 +852,12 @@ def _gi_build_stub(
838852
# Avoid duplicates
839853
continue
840854
names.append(n)
841-
type = _build_type(GIRepository.property_info_get_type(p))
855+
type = _build_type(GIRepository.PropertyInfo.get_type_info(p))
842856
t = _type_to_python(type, current_namespace, needed_namespaces)
843-
setter = GIRepository.property_info_get_setter(p)
857+
setter = GIRepository.PropertyInfo.get_setter(p)
844858
if setter:
845-
arg_info = GIRepository.callable_info_get_arg(setter, 0)
846-
if GIRepository.arg_info_may_be_null(arg_info):
859+
arg_info = GIRepository.CallableInfo.get_arg(setter, 0)
860+
if GIRepository.ArgInfo.may_be_null(arg_info):
847861
s.append(f"{n}: typing.Optional[{t}] = ...")
848862
else:
849863
s.append(f"{n}: {t} = ...")
@@ -876,9 +890,12 @@ def _gi_build_stub(
876890
needed_namespaces.add("GObject")
877891
base = "GObject.GFlags"
878892

893+
if str(obj).startswith("<flag"):
894+
base = "enum.IntFlag"
895+
879896
ret += f"class {name}({base}):\n"
880897
for key in sorted(vars(obj)):
881-
if key.startswith("__") or key[0].isdigit():
898+
if key.startswith(("__", "_")) or key[0].isdigit():
882899
continue
883900

884901
override = _check_override(full_name, key, overrides)
@@ -919,11 +936,14 @@ def _gi_build_stub(
919936
needed_namespaces.add("GObject")
920937
base = "GObject.GEnum"
921938

939+
if str(obj).startswith("<enum"):
940+
base = "enum.IntEnum"
941+
922942
# Some Enums can be empty in the end
923943
ret += f"class {name}({base}):\n"
924944
length_before = len(ret)
925945
for key in sorted(vars(obj)):
926-
if key.startswith("__") or key[0].isdigit():
946+
if key.startswith(("__", "_")) or key[0].isdigit():
927947
continue
928948

929949
override = _check_override(full_name, key, overrides)
@@ -946,7 +966,7 @@ def _gi_build_stub(
946966
ret += f" {key} = ... # FIXME Enum\n"
947967

948968
if len(ret) == length_before:
949-
# No attributes where found
969+
# No attributes were found
950970
ret += " ...\n"
951971

952972
ret += "\n"
@@ -987,14 +1007,13 @@ def _get_gname(obj: Type[Any]) -> Optional[str]:
9871007
return obj.__gtype__.name # type: ignore
9881008

9891009

990-
def start(
991-
module: str, version: str, init: str | None, overrides: dict[str, str]
992-
) -> str:
993-
gi.require_version(module, version)
1010+
def start(module: str, version: str, init: str | None, overrides: dict[str, str]) -> str:
1011+
repo = GIRepository.Repository()
1012+
repo.require(module, version, 0) # type: ignore
9941013
m = importlib.import_module(f".{module}", "gi.repository")
9951014
if init:
9961015
exec(init)
997-
return _build(m, args.module, overrides)
1016+
return _build(repo, m, module, overrides)
9981017

9991018

10001019
if __name__ == "__main__":
@@ -1020,7 +1039,7 @@ def start(
10201039
except FileNotFoundError:
10211040
pass
10221041
output = start(args.module, args.version, args.init, overrides)
1023-
print("Running with this overrides:")
1042+
print("Running with these overrides:")
10241043
pprint.pprint(overrides)
10251044
with open(args.update, "w+") as file:
10261045
file.write(output)

0 commit comments

Comments
 (0)