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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
import inspect
import sys
"""Helpers for determining base classes by importing the class.
When passed something like:
PySide6.QtCore.QStateMachine.SignalEvent
try to import the underlying module and return a
handle to the object. In a loop, import
PySide6.QtCore.QStateMachine.SignalEvent
PySide6.QtCore.QStateMachine
PySide6.QtCore
until the import succeeds and walk up the attributes
to obtain the object."""
TEST_DRIVER_USAGE = """Usage: import_inheritance.py class_name [current_module]
Example:
python import_inheritance.py PySide6.QtWidgets.QWizard PySide6.QtWidgets
"""
class InheritanceException(Exception):
pass
def _importClassOrModule(name):
components = name.split('.')
for i in range(len(components), 0, -1):
importPath = '.'.join(components[: i])
try:
__import__(importPath)
except ImportError:
continue
if i == len(components):
return sys.modules[importPath]
remaining = components[i:]
cls = sys.modules[importPath]
for component in remaining:
try:
cls = getattr(cls, component)
except Exception: # No such attribute
return None
return cls
return None
def _import_class_or_module(name, currmodule):
"""
Import a class using its fully-qualified *name*.
"""
todoc = _importClassOrModule(name)
if not todoc and currmodule is not None:
todoc = _importClassOrModule(f"{currmodule}.{name}")
if not todoc:
moduleStr = f'(module {currmodule})' if currmodule else ''
raise InheritanceException(f'Could not import class {name} specified for '
f'inheritance diagram {moduleStr}.')
if inspect.isclass(todoc):
return [todoc]
elif inspect.ismodule(todoc):
classes = []
for cls in todoc.__dict__.values():
if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
classes.append(cls)
return classes
raise InheritanceException(f'{name} specified for inheritance diagram is '
'not a class or module')
def _import_classes(class_names, currmodule):
"""Import a list of classes."""
classes = []
for name in class_names:
classes.extend(_import_class_or_module(name, currmodule))
return classes
def _class_name(cls, parts=0):
"""Given a class object, return a fully-qualified name.
This works for things I've tested in matplotlib so far, but may not be
completely general.
"""
module = cls.__module__
if module == '__builtin__':
fullname = cls.__name__
else:
fullname = f"{module}.{cls.__qualname__}"
if parts == 0:
return fullname
name_parts = fullname.split('.')
return '.'.join(name_parts[-parts:])
def _class_info(classes, builtins=None, show_builtins=False, parts=0):
"""Return name and bases for all classes that are ancestors of
*classes*.
*parts* gives the number of dotted name parts that is removed from the
displayed node names.
"""
all_classes = {}
builtins_list = builtins.values() if builtins else []
def recurse(cls):
if not show_builtins and cls in builtins_list:
return
nodename = _class_name(cls, parts)
fullname = _class_name(cls, 0)
baselist = []
all_classes[cls] = (nodename, fullname, baselist)
for base in cls.__bases__:
if not show_builtins and base in builtins_list:
continue
if base.__name__ == "Object" and base.__module__ == "Shiboken":
continue
baselist.append(_class_name(base, parts))
if base not in all_classes:
recurse(base)
for cls in classes:
recurse(cls)
return list(all_classes.values())
def get_inheritance_entries_by_import(class_names, currmodule,
builtins=None,
show_builtins=False, parts=0):
classes = _import_classes(class_names, currmodule)
class_info = _class_info(classes, builtins, show_builtins, parts)
if not class_info:
raise InheritanceException('No classes found for '
'inheritance diagram')
return class_info
if __name__ == "__main__":
module = None
if len(sys.argv) < 2:
print(TEST_DRIVER_USAGE)
sys.exit(-1)
class_name = sys.argv[1]
if len(sys.argv) >= 3:
module = sys.argv[2]
entries = get_inheritance_entries_by_import([class_name], module, None,
False, 2)
for e in entries:
print(e)
|