Skip to content

Commit 997fd5d

Browse files
Add Qt 6.10 support (added many property flags, and line numbers)
1 parent 833a9ec commit 997fd5d

37 files changed

+604
-288
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ jobs:
1717
strategy:
1818
matrix:
1919
os: [ubuntu-latest, windows-latest, macos-latest]
20-
pyside6_version: [6.8.3]
20+
pyside6_version: [6.10.0]
2121
include:
2222
- os: ubuntu-latest
23-
pyside6_version: 6.7.3
23+
pyside6_version: 6.8.3
2424
- os: ubuntu-latest
2525
pyside6_version: 6.9.3
26+
- os: ubuntu-latest
27+
pyside6_version: 6.10.0
2628
build_wheel: true
2729
runs-on: ${{ matrix.os }}
2830
name: "Test on ${{ matrix.os }} with PySide6 ${{ matrix.pyside6_version }}"

pyside6_qml_stubgen/__init__.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def parse_module(
3333
ret.append(
3434
qmlregistrar_types.Module(
3535
classes=[parse_class(cls, depends_on, extra_info)],
36-
outputRevision=68,
36+
outputRevision=69,
3737
QML_IMPORT_MAJOR_VERSION=major,
3838
QML_IMPORT_MINOR_VERSION=minor,
3939
QT_MODULES=sorted(
@@ -65,6 +65,7 @@ def parse_class(
6565
className=meta.className(),
6666
qualifiedClassName=cls.__qualname__,
6767
object=True,
68+
lineNumber=extra_info.find_line_number(cls),
6869
superClasses=[
6970
qmlregistrar_types.SuperClass(
7071
access="public", name=meta.superClass().className()
@@ -112,7 +113,7 @@ def parse_enum(enum: QtCore.QMetaEnum) -> qmlregistrar_types.Enum:
112113
isClass=enum.isScoped(),
113114
isFlag=enum.isFlag(),
114115
name=str(enum.enumName()),
115-
type="quint16",
116+
lineNumber=0, # TODO: Bit tricky as we don't get a reference to the class or module+name anywhere
116117
values=[str(enum.key(i)) for i in range(enum.keyCount())],
117118
)
118119

@@ -142,9 +143,20 @@ def parse_property(
142143
return qmlregistrar_types.Property(
143144
name=str(prop.name()),
144145
type=resolve_type_name(
145-
prop.typeName(), t, depends_on, extra_info # type: ignore[arg-type]
146+
prop.typeName(),
147+
t,
148+
depends_on,
149+
extra_info, # type: ignore[arg-type]
146150
),
147151
index=prop.propertyIndex(),
152+
lineNumber=extra_info.find_line_number(p.fget),
153+
constant=prop.isConstant(),
154+
designable=prop.isDesignable(),
155+
final=prop.isFinal(),
156+
required=prop.isRequired(),
157+
scriptable=prop.isScriptable(),
158+
stored=prop.isStored(),
159+
user=prop.isUser(),
148160
notify=(
149161
bytes(prop.notifySignal().name().data()).decode()
150162
if prop.hasNotifySignal()
@@ -170,6 +182,9 @@ def parse_method(
170182
return qmlregistrar_types.Method(
171183
access="public",
172184
name=bytes(meth.name().data()).decode(),
185+
index=meth.methodIndex(),
186+
lineNumber=extra_info.find_line_number(m),
187+
isConst=meth.isConst(),
173188
arguments=[
174189
qmlregistrar_types.Argument(
175190
name=bytes(meth.parameterNames()[i].data()).decode() or n,

pyside6_qml_stubgen/pyside_patching.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class ExtraCollectedInfo:
2828
module_dependencies: typing.DefaultDict[str, set[str]] = dataclasses.field(
2929
default_factory=lambda: collections.defaultdict(set)
3030
)
31+
line_numbers: dict[QtCore.Signal | typing.Callable | type, int] = dataclasses.field(
32+
default_factory=dict
33+
)
3134

3235
def lookup_cls_module(self, cls: type[QtCore.QObject]) -> str | None:
3336
for (uri, major, minor), clses in self.registered_classes.items():
@@ -41,8 +44,10 @@ def add_cls(
4144
version_major: int,
4245
version_minor: int,
4346
type_obj: type[QtCore.QObject],
47+
line_number: int,
4448
) -> None:
4549
self.registered_classes[uri, version_major, version_minor].add(type_obj)
50+
self.line_numbers[type_obj] = line_number
4651
for base_cls in type_obj.__mro__:
4752
if isinstance(base_cls, type) and issubclass(base_cls, QtCore.QObject):
4853
self.delayed_registrations[type_obj].add(base_cls)
@@ -57,6 +62,21 @@ def resolve_delayed(self) -> None:
5762
self.registered_classes[module].add(linked_cls)
5863
self.delayed_registrations.clear()
5964

65+
def find_line_number(self, obj: QtCore.Signal | typing.Callable | type) -> int:
66+
if obj in self.line_numbers:
67+
return self.line_numbers[obj]
68+
elif isinstance(obj, types.FunctionType):
69+
return obj.__code__.co_firstlineno
70+
elif isinstance(obj, type):
71+
if lineno := getattr(obj, "__firstlineno__", None):
72+
return lineno
73+
try:
74+
return inspect.getsourcelines(obj)[1]
75+
except OSError:
76+
return 0
77+
else:
78+
return 0
79+
6080

6181
def patch_with(mod: types.ModuleType) -> typing.Callable[[typing.Callable], None]:
6282
def w(fn: typing.Callable) -> None:
@@ -67,6 +87,18 @@ def w(fn: typing.Callable) -> None:
6787
return w
6888

6989

90+
def go_up(frame: types.FrameType | None, levels: int) -> types.FrameType | None:
91+
if levels == 0:
92+
return frame
93+
return go_up(frame.f_back, levels - 1) if frame else None
94+
95+
96+
def get_line_number(stack_levels: int = 3) -> int:
97+
frame = go_up(inspect.currentframe(), stack_levels)
98+
assert frame is not None, "No caller frame"
99+
return frame.f_lineno
100+
101+
70102
def patch_functions(info: ExtraCollectedInfo) -> None:
71103
@patch_with(QtQml)
72104
def qmlRegisterSingletonInstance(
@@ -81,7 +113,7 @@ def qmlRegisterSingletonInstance(
81113
) -> None:
82114
info.extra_class_infos[type_obj].append(("QML.Singleton", "true"))
83115
info.extra_class_infos[type_obj].append(("QML.Element", qml_name))
84-
info.add_cls(uri, version_major, version_minor, type_obj)
116+
info.add_cls(uri, version_major, version_minor, type_obj, get_line_number())
85117
old_fn(type_obj, uri, version_major, version_minor, qml_name, callback)
86118

87119
@patch_with(QtQml)
@@ -97,7 +129,7 @@ def qmlRegisterSingletonType(
97129
) -> None:
98130
info.extra_class_infos[type_obj].append(("QML.Singleton", "true"))
99131
info.extra_class_infos[type_obj].append(("QML.Element", qml_name))
100-
info.add_cls(uri, version_major, version_minor, type_obj)
132+
info.add_cls(uri, version_major, version_minor, type_obj, get_line_number())
101133
old_fn(
102134
type_obj,
103135
uri,
@@ -118,7 +150,7 @@ def qmlRegisterType(
118150
old_fn: typing.Callable,
119151
) -> None:
120152
info.extra_class_infos[type_obj].append(("QML.Element", qml_name))
121-
info.add_cls(uri, version_major, version_minor, type_obj)
153+
info.add_cls(uri, version_major, version_minor, type_obj, get_line_number())
122154
old_fn(type_obj, uri, version_major, version_minor, qml_name)
123155

124156
@patch_with(QtQml)
@@ -148,16 +180,11 @@ def qmlRegisterUncreatableType(
148180
info.extra_class_infos[type_obj].append(("QML.Creatable", "false"))
149181
info.extra_class_infos[type_obj].append(("QML.UncreatableReason", message))
150182
info.extra_class_infos[type_obj].append(("QML.Element", qml_name))
151-
info.add_cls(uri, version_major, version_minor, type_obj)
183+
info.add_cls(uri, version_major, version_minor, type_obj, get_line_number())
152184
old_fn(type_obj, uri, version_major, version_minor, qml_name, message)
153185

154186

155187
def patch_class_decorators(info: ExtraCollectedInfo) -> None:
156-
def go_up(frame: types.FrameType | None, levels: int) -> types.FrameType | None:
157-
if levels == 0:
158-
return frame
159-
return go_up(frame.f_back, levels - 1) if frame else None
160-
161188
def get_module(stack_levels: int = 3) -> tuple[str, int, int]:
162189
frame = go_up(inspect.currentframe(), stack_levels)
163190
assert frame is not None, "No caller frame"
@@ -192,7 +219,7 @@ def QmlElement(
192219
) -> type[T_TypeQObject]:
193220
info.extra_class_infos[cls].append(("QML.Element", "auto"))
194221
mod_info = get_module()
195-
info.add_cls(*mod_info, cls)
222+
info.add_cls(*mod_info, cls, get_line_number())
196223
with module_in_globals(*mod_info):
197224
return old_fn(cls)
198225

@@ -202,7 +229,7 @@ def QmlAnonymous(
202229
) -> type[T_TypeQObject]:
203230
info.extra_class_infos[cls].append(("QML.Element", "anonymous"))
204231
mod_info = get_module()
205-
info.add_cls(*mod_info, cls)
232+
info.add_cls(*mod_info, cls, get_line_number())
206233
with module_in_globals(*mod_info):
207234
return old_fn(cls)
208235

@@ -213,7 +240,7 @@ def QmlNamedElement(
213240
def w(cls: type[T_TypeQObject]) -> type[T_TypeQObject]:
214241
info.extra_class_infos[cls].append(("QML.Element", name))
215242
mod_info = get_module(stack_levels=2)
216-
info.add_cls(*mod_info, cls)
243+
info.add_cls(*mod_info, cls, get_line_number(stack_levels=2))
217244
with module_in_globals(*mod_info):
218245
return old_fn(name)(cls)
219246

@@ -242,7 +269,7 @@ def w(cls: type[T_TypeQObject]) -> type[T_TypeQObject]:
242269
except KeyError:
243270
info.delayed_registrations[cls].add(type_obj)
244271
else:
245-
info.add_cls(*module, cls)
272+
info.add_cls(*module, cls, get_line_number(stack_levels=4))
246273
info.extra_class_infos[cls].append(
247274
("QML.Foreign", type_obj.staticMetaObject.className()) # type: ignore[attr-defined]
248275
)
@@ -260,7 +287,7 @@ def w(cls: type[T_TypeQObject]) -> type[T_TypeQObject]:
260287
except KeyError:
261288
info.delayed_registrations[cls].add(type_obj)
262289
else:
263-
info.add_cls(*module, cls)
290+
info.add_cls(*module, cls, get_line_number(stack_levels=4))
264291
info.extra_class_infos[cls].append(
265292
("QML.Extended", type_obj.staticMetaObject.className()) # type: ignore[attr-defined]
266293
)
@@ -278,7 +305,7 @@ def w(cls: type[T_TypeQObject]) -> type[T_TypeQObject]:
278305
except KeyError:
279306
info.delayed_registrations[cls].add(type_obj)
280307
else:
281-
info.add_cls(*module, cls)
308+
info.add_cls(*module, cls, get_line_number(stack_levels=4))
282309
info.extra_class_infos[cls].append(
283310
("QML.Attached", type_obj.staticMetaObject.className()) # type: ignore[attr-defined]
284311
)
@@ -344,6 +371,7 @@ def __subclasscheck__(self, subclass: type) -> bool:
344371
def __call__(self, *types: type | str, **kwargs: typing.Any) -> QtCore.Signal:
345372
sig = QtSignal(*types, **kwargs) # type: ignore[arg-type]
346373
info.signal_types[sig] = (types, None)
374+
info.line_numbers[sig] = get_line_number(stack_levels=2)
347375

348376
return sig
349377

pyside6_qml_stubgen/qmlregistrar_types.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,25 @@ class Method:
1717
name: str
1818
arguments: typing.Sequence[Argument]
1919
returnType: str
20+
index: int
21+
lineNumber: int
22+
isConst: bool
2023

2124

2225
@dataclasses.dataclass(frozen=True)
2326
class Property:
27+
constant: bool
28+
designable: bool
29+
final: bool
30+
index: int
31+
lineNumber: int
2432
name: str
33+
required: bool
34+
scriptable: bool
35+
stored: bool
2536
type: str
26-
index: int
37+
user: bool
38+
bindable: str | None = None
2739
notify: str | None = None
2840
read: str | None = None
2941
write: str | None = None
@@ -34,8 +46,9 @@ class Enum:
3446
isClass: bool
3547
isFlag: bool
3648
name: str
37-
type: str
49+
lineNumber: int
3850
values: typing.Sequence[str]
51+
type: str | None = None
3952

4053

4154
@dataclasses.dataclass(frozen=True)
@@ -55,6 +68,7 @@ class Class:
5568
className: str
5669
qualifiedClassName: str
5770
object: bool
71+
lineNumber: int
5872
superClasses: typing.Sequence[SuperClass]
5973
classInfos: typing.Sequence[ClassInfo]
6074
enums: typing.Sequence[Enum]
@@ -66,7 +80,7 @@ class Class:
6680
@dataclasses.dataclass(frozen=True)
6781
class Module:
6882
classes: typing.Sequence[Class]
69-
outputRevision: typing.Literal[68]
83+
outputRevision: typing.Literal[69]
7084
QML_IMPORT_MAJOR_VERSION: int
7185
QML_IMPORT_MINOR_VERSION: int
7286
QT_MODULES: typing.Sequence[str]
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"modules": {
3+
"in.advanced": {
4+
"modification_time": 1763380832.973606,
5+
"path": "C:\\Users\\matthew.joyce\\AppData\\Local\\Temp\\pytest-of-matthew.joyce\\pytest-973\\test_run_and_compare0\\in\\advanced.py",
6+
"dependencies": [
7+
"PySide6",
8+
"PySide6.QtCore",
9+
"PySide6.QtQml"
10+
]
11+
},
12+
"PySide6": {
13+
"modification_time": 1763377813.7888794,
14+
"path": "C:\\Users\\matthew.joyce\\Repos\\pyside6-qml-stubgen\\venv\\lib\\site-packages\\PySide6\\__init__.py",
15+
"dependencies": []
16+
},
17+
"PySide6.QtCore": {
18+
"modification_time": 1763377799.6107633,
19+
"path": "C:\\Users\\matthew.joyce\\Repos\\pyside6-qml-stubgen\\venv\\lib\\site-packages\\PySide6\\QtCore.pyd",
20+
"dependencies": []
21+
},
22+
"PySide6.QtQml": {
23+
"modification_time": 1763377799.7451968,
24+
"path": "C:\\Users\\matthew.joyce\\Repos\\pyside6-qml-stubgen\\venv\\lib\\site-packages\\PySide6\\QtQml.pyd",
25+
"dependencies": []
26+
},
27+
"in.advanced2": {
28+
"modification_time": 1763380832.9740903,
29+
"path": "C:\\Users\\matthew.joyce\\AppData\\Local\\Temp\\pytest-of-matthew.joyce\\pytest-973\\test_run_and_compare0\\in\\advanced2.py",
30+
"dependencies": [
31+
"PySide6",
32+
"PySide6.QtCore",
33+
"PySide6.QtQml"
34+
]
35+
},
36+
"in.clses": {
37+
"modification_time": 1763380832.974605,
38+
"path": "C:\\Users\\matthew.joyce\\AppData\\Local\\Temp\\pytest-of-matthew.joyce\\pytest-973\\test_run_and_compare0\\in\\clses.py",
39+
"dependencies": [
40+
"PySide6",
41+
"PySide6.QtCore",
42+
"PySide6.QtGui",
43+
"PySide6.QtQml",
44+
"enum"
45+
]
46+
},
47+
"enum": {
48+
"modification_time": 1680655642.0,
49+
"path": "C:\\Program Files\\Python310\\lib\\enum.py",
50+
"dependencies": []
51+
},
52+
"PySide6.QtGui": {
53+
"modification_time": 1763377799.6517696,
54+
"path": "C:\\Users\\matthew.joyce\\Repos\\pyside6-qml-stubgen\\venv\\lib\\site-packages\\PySide6\\QtGui.pyd",
55+
"dependencies": []
56+
},
57+
"in.clses2": {
58+
"modification_time": 1763380832.9750905,
59+
"path": "C:\\Users\\matthew.joyce\\AppData\\Local\\Temp\\pytest-of-matthew.joyce\\pytest-973\\test_run_and_compare0\\in\\clses2.py",
60+
"dependencies": [
61+
"PySide6",
62+
"PySide6.QtCore",
63+
"PySide6.QtQml"
64+
]
65+
},
66+
"in.submodule": {
67+
"modification_time": 1763380832.9750905,
68+
"path": "C:\\Users\\matthew.joyce\\AppData\\Local\\Temp\\pytest-of-matthew.joyce\\pytest-973\\test_run_and_compare0\\in\\submodule.py",
69+
"dependencies": [
70+
"PySide6",
71+
"PySide6.QtCore",
72+
"PySide6.QtQml",
73+
"in",
74+
"in.clses"
75+
]
76+
},
77+
"in": {
78+
"modification_time": 1763380832.97559,
79+
"path": "C:\\Users\\matthew.joyce\\AppData\\Local\\Temp\\pytest-of-matthew.joyce\\pytest-973\\test_run_and_compare0\\in\\__init__.py",
80+
"dependencies": [
81+
"in",
82+
"in.clses",
83+
"in.clses2"
84+
]
85+
}
86+
},
87+
"generating_version": "0.1.0a9.dev0+g9d7de2861.d20251020"
88+
}
File renamed without changes.

0 commit comments

Comments
 (0)