1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import os
import subprocess
import sys
from argparse import ArgumentParser, RawTextHelpFormatter
from pathlib import Path
import xml.sax
from xml.sax.handler import ContentHandler
DESC = """Print a list of module short names ordered by typesystem dependencies
for which documentation can be built by intersecting the PySide6 modules with
the modules built in Qt."""
ROOT_DIR = Path(__file__).parents[1].resolve()
SOURCE_DIR = ROOT_DIR / "sources" / "pyside6" / "PySide6"
class TypeSystemContentHandler(ContentHandler):
"""XML SAX content handler that extracts required modules from the
"load-typesystem" elements of the typesystem_file. Nodes that start
with Qt and are marked as generate == "no" are considered required."""
def __init__(self):
self.required_modules = []
def startElement(self, name, attrs):
if name == "load-typesystem":
generate = attrs.get("generate", "").lower()
if generate == "no" or generate == "false":
load_file_name = attrs.get("name") # "QtGui/typesystem_gui.xml"
if load_file_name.startswith("Qt"):
slash = load_file_name.find("/")
if slash > 0:
self.required_modules.append(load_file_name[:slash])
def required_typesystems(module):
"""Determine the required Qt modules by looking at the "load-typesystem"
elements of the typesystem_file."""
name = module[2:].lower()
typesystem_file = SOURCE_DIR / module / f"typesystem_{name}.xml"
# Use a SAX parser since that works despite undefined entity
# errors for typesystem entities.
handler = TypeSystemContentHandler()
try:
parser = xml.sax.make_parser()
parser.setContentHandler(handler)
parser.parse(typesystem_file)
except Exception as e:
print(f"Error parsing {typesystem_file}: {e}", file=sys.stderr)
return handler.required_modules
def sort_modules(dependency_dict):
"""Sort the modules by dependencies using brute force: Keep adding
modules all of whose requirements are present to the result list
until done."""
result = []
while True:
found = False
for module, dependencies in dependency_dict.items():
if module not in result:
if all(dependency in result for dependency in dependencies):
result.append(module)
found = True
if not found:
break
if len(result) < len(dependency_dict) and verbose:
for desired_module in dependency_dict.keys():
if desired_module not in result:
print(f"Not documenting {desired_module} (missing dependency)",
file=sys.stderr)
return result
if __name__ == "__main__":
argument_parser = ArgumentParser(description=DESC,
formatter_class=RawTextHelpFormatter)
argument_parser.add_argument("--verbose", "-v", action="store_true",
help="Verbose")
argument_parser.add_argument("qt_include_dir", help="Qt Include dir",
nargs='?', type=str)
options = argument_parser.parse_args()
verbose = options.verbose
qt_include_dir = None
if options.qt_include_dir:
qt_include_dir = Path(options.qt_include_dir)
if not qt_include_dir.is_dir():
print(f"Invalid include directory passed: {options.qt_include_dir}",
file=sys.stderr)
sys.exit(-1)
else:
verbose = True # Called by hand to find out about available modules
query_cmd = ["qtpaths", "-query", "QT_INSTALL_HEADERS"]
output = subprocess.check_output(query_cmd, stderr=subprocess.STDOUT,
universal_newlines=True)
qt_include_dir = Path(output.strip())
if not qt_include_dir.is_dir():
print("Cannot determine include directory", file=sys.stderr)
sys.exit(-1)
# Build a typesystem dependency dict of the available modules in order
# to be able to sort_modules by dependencies. This is required as
# otherwise shiboken will read the required typesystems with
# generate == "no" and thus omit modules.
module_dependency_dict = {}
for m in SOURCE_DIR.glob("Qt*"):
module = m.name
qt_include_path = qt_include_dir / module
if qt_include_path.is_dir():
module_dependency_dict[module] = required_typesystems(module)
elif verbose:
print(f"Not documenting {module} (not built)", file=sys.stderr)
modules = sort_modules(module_dependency_dict)
print(" ".join([m[2:] for m in modules]))
|