OLD | NEW |
| (Empty) |
1 """Astroid hooks for the Python 2 GObject introspection bindings. | |
2 | |
3 Helps with understanding everything imported from 'gi.repository' | |
4 """ | |
5 | |
6 import inspect | |
7 import sys | |
8 import re | |
9 | |
10 from astroid import MANAGER, AstroidBuildingException | |
11 from astroid.builder import AstroidBuilder | |
12 | |
13 | |
14 _inspected_modules = {} | |
15 | |
16 _identifier_re = r'^[A-Za-z_]\w*$' | |
17 | |
18 def _gi_build_stub(parent): | |
19 """ | |
20 Inspect the passed module recursively and build stubs for functions, | |
21 classes, etc. | |
22 """ | |
23 classes = {} | |
24 functions = {} | |
25 constants = {} | |
26 methods = {} | |
27 for name in dir(parent): | |
28 if name.startswith("__"): | |
29 continue | |
30 | |
31 # Check if this is a valid name in python | |
32 if not re.match(_identifier_re, name): | |
33 continue | |
34 | |
35 try: | |
36 obj = getattr(parent, name) | |
37 except: | |
38 continue | |
39 | |
40 if inspect.isclass(obj): | |
41 classes[name] = obj | |
42 elif (inspect.isfunction(obj) or | |
43 inspect.isbuiltin(obj)): | |
44 functions[name] = obj | |
45 elif (inspect.ismethod(obj) or | |
46 inspect.ismethoddescriptor(obj)): | |
47 methods[name] = obj | |
48 elif type(obj) in [int, str]: | |
49 constants[name] = obj | |
50 elif (str(obj).startswith("<flags") or | |
51 str(obj).startswith("<enum ") or | |
52 str(obj).startswith("<GType ") or | |
53 inspect.isdatadescriptor(obj)): | |
54 constants[name] = 0 | |
55 elif callable(obj): | |
56 # Fall back to a function for anything callable | |
57 functions[name] = obj | |
58 else: | |
59 # Assume everything else is some manner of constant | |
60 constants[name] = 0 | |
61 | |
62 ret = "" | |
63 | |
64 if constants: | |
65 ret += "# %s contants\n\n" % parent.__name__ | |
66 for name in sorted(constants): | |
67 if name[0].isdigit(): | |
68 # GDK has some busted constant names like | |
69 # Gdk.EventType.2BUTTON_PRESS | |
70 continue | |
71 | |
72 val = constants[name] | |
73 | |
74 strval = str(val) | |
75 if type(val) is str: | |
76 strval = '"%s"' % str(val).replace("\\", "\\\\") | |
77 ret += "%s = %s\n" % (name, strval) | |
78 | |
79 if ret: | |
80 ret += "\n\n" | |
81 if functions: | |
82 ret += "# %s functions\n\n" % parent.__name__ | |
83 for name in sorted(functions): | |
84 func = functions[name] | |
85 ret += "def %s(*args, **kwargs):\n" % name | |
86 ret += " pass\n" | |
87 | |
88 if ret: | |
89 ret += "\n\n" | |
90 if methods: | |
91 ret += "# %s methods\n\n" % parent.__name__ | |
92 for name in sorted(methods): | |
93 func = methods[name] | |
94 ret += "def %s(self, *args, **kwargs):\n" % name | |
95 ret += " pass\n" | |
96 | |
97 if ret: | |
98 ret += "\n\n" | |
99 if classes: | |
100 ret += "# %s classes\n\n" % parent.__name__ | |
101 for name in sorted(classes): | |
102 ret += "class %s(object):\n" % name | |
103 | |
104 classret = _gi_build_stub(classes[name]) | |
105 if not classret: | |
106 classret = "pass\n" | |
107 | |
108 for line in classret.splitlines(): | |
109 ret += " " + line + "\n" | |
110 ret += "\n" | |
111 | |
112 return ret | |
113 | |
114 # Overwrite Module.module_import to _actually_ import the introspected module if | |
115 # it's a gi module, then build stub code by examining its info and get an astng | |
116 # from that | |
117 | |
118 from astroid.scoped_nodes import Module | |
119 _orig_import_module = Module.import_module | |
120 | |
121 def _new_import_module(self, modname, relative_only=False, level=None): | |
122 # Could be a static piece of gi.repository or whatever unrelated module, | |
123 # let that fall through | |
124 try: | |
125 return _orig_import_module(self, modname, relative_only, level) | |
126 except AstroidBuildingException: | |
127 # we only consider gi.repository submodules | |
128 if not modname.startswith('gi.repository.'): | |
129 if relative_only and level is None: | |
130 level = 0 | |
131 modname = self.relative_to_absolute_name(modname, level) | |
132 if not modname.startswith('gi.repository.'): | |
133 raise | |
134 # build astroid representation unless we already tried so | |
135 if modname not in _inspected_modules: | |
136 modnames = [modname] | |
137 # GLib and GObject have some special case handling | |
138 # in pygobject that we need to cope with | |
139 if modname == 'gi.repository.GLib': | |
140 modnames.append('gi._glib') | |
141 elif modname == 'gi.repository.GObject': | |
142 modnames.append('gi._gobject') | |
143 try: | |
144 modcode = '' | |
145 for m in modnames: | |
146 __import__(m) | |
147 modcode += _gi_build_stub(sys.modules[m]) | |
148 except ImportError: | |
149 astng = _inspected_modules[modname] = None | |
150 else: | |
151 astng = AstroidBuilder(MANAGER).string_build(modcode, modname) | |
152 _inspected_modules[modname] = astng | |
153 else: | |
154 astng = _inspected_modules[modname] | |
155 if astng is None: | |
156 raise AstroidBuildingException('Failed to import module %r' % modname) | |
157 return astng | |
158 | |
159 Module.import_module = _new_import_module | |
OLD | NEW |