gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / astroid / brain / brain_gi.py @ 745
History | View | Annotate | Download (5.89 KB)
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 itertools |
8 |
import sys |
9 |
import re |
10 |
import warnings |
11 |
|
12 |
from astroid import MANAGER, AstroidBuildingException, nodes |
13 |
from astroid.builder import AstroidBuilder |
14 |
|
15 |
|
16 |
_inspected_modules = {} |
17 |
|
18 |
_identifier_re = r'^[A-Za-z_]\w*$'
|
19 |
|
20 |
def _gi_build_stub(parent): |
21 |
"""
|
22 |
Inspect the passed module recursively and build stubs for functions,
|
23 |
classes, etc.
|
24 |
"""
|
25 |
classes = {} |
26 |
functions = {} |
27 |
constants = {} |
28 |
methods = {} |
29 |
for name in dir(parent): |
30 |
if name.startswith("__"): |
31 |
continue
|
32 |
|
33 |
# Check if this is a valid name in python
|
34 |
if not re.match(_identifier_re, name): |
35 |
continue
|
36 |
|
37 |
try:
|
38 |
obj = getattr(parent, name)
|
39 |
except:
|
40 |
continue
|
41 |
|
42 |
if inspect.isclass(obj):
|
43 |
classes[name] = obj |
44 |
elif (inspect.isfunction(obj) or |
45 |
inspect.isbuiltin(obj)): |
46 |
functions[name] = obj |
47 |
elif (inspect.ismethod(obj) or |
48 |
inspect.ismethoddescriptor(obj)): |
49 |
methods[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 isinstance(obj, (int, str)): |
56 |
constants[name] = obj |
57 |
elif callable(obj): |
58 |
# Fall back to a function for anything callable
|
59 |
functions[name] = obj |
60 |
else:
|
61 |
# Assume everything else is some manner of constant
|
62 |
constants[name] = 0
|
63 |
|
64 |
ret = ""
|
65 |
|
66 |
if constants:
|
67 |
ret += "# %s contants\n\n" % parent.__name__
|
68 |
for name in sorted(constants): |
69 |
if name[0].isdigit(): |
70 |
# GDK has some busted constant names like
|
71 |
# Gdk.EventType.2BUTTON_PRESS
|
72 |
continue
|
73 |
|
74 |
val = constants[name] |
75 |
|
76 |
strval = str(val)
|
77 |
if isinstance(val, str): |
78 |
strval = '"%s"' % str(val).replace("\\", "\\\\") |
79 |
ret += "%s = %s\n" % (name, strval)
|
80 |
|
81 |
if ret:
|
82 |
ret += "\n\n"
|
83 |
if functions:
|
84 |
ret += "# %s functions\n\n" % parent.__name__
|
85 |
for name in sorted(functions): |
86 |
ret += "def %s(*args, **kwargs):\n" % name
|
87 |
ret += " pass\n"
|
88 |
|
89 |
if ret:
|
90 |
ret += "\n\n"
|
91 |
if methods:
|
92 |
ret += "# %s methods\n\n" % parent.__name__
|
93 |
for name in sorted(methods): |
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 |
def _import_gi_module(modname): |
115 |
# we only consider gi.repository submodules
|
116 |
if not modname.startswith('gi.repository.'): |
117 |
raise AstroidBuildingException()
|
118 |
# build astroid representation unless we already tried so
|
119 |
if modname not in _inspected_modules: |
120 |
modnames = [modname] |
121 |
optional_modnames = [] |
122 |
|
123 |
# GLib and GObject may have some special case handling
|
124 |
# in pygobject that we need to cope with. However at
|
125 |
# least as of pygobject3-3.13.91 the _glib module doesn't
|
126 |
# exist anymore, so if treat these modules as optional.
|
127 |
if modname == 'gi.repository.GLib': |
128 |
optional_modnames.append('gi._glib')
|
129 |
elif modname == 'gi.repository.GObject': |
130 |
optional_modnames.append('gi._gobject')
|
131 |
|
132 |
try:
|
133 |
modcode = ''
|
134 |
for m in itertools.chain(modnames, optional_modnames): |
135 |
try:
|
136 |
__import__(m)
|
137 |
with warnings.catch_warnings():
|
138 |
# Just inspecting the code can raise gi deprecation
|
139 |
# warnings, so ignore them.
|
140 |
try:
|
141 |
from gi import PyGIDeprecationWarning |
142 |
warnings.simplefilter("ignore", PyGIDeprecationWarning)
|
143 |
except Exception: |
144 |
pass
|
145 |
|
146 |
modcode += _gi_build_stub(sys.modules[m]) |
147 |
except ImportError: |
148 |
if m not in optional_modnames: |
149 |
raise
|
150 |
except ImportError: |
151 |
astng = _inspected_modules[modname] = None
|
152 |
else:
|
153 |
astng = AstroidBuilder(MANAGER).string_build(modcode, modname) |
154 |
_inspected_modules[modname] = astng |
155 |
else:
|
156 |
astng = _inspected_modules[modname] |
157 |
if astng is None: |
158 |
raise AstroidBuildingException('Failed to import module %r' % modname) |
159 |
return astng
|
160 |
|
161 |
def _looks_like_require_version(node): |
162 |
# Return whether this looks like a call to gi.require_version(<name>, <version>)
|
163 |
# Only accept function calls with two constant arguments
|
164 |
if len(node.args) != 2: |
165 |
return False |
166 |
|
167 |
if not all(isinstance(arg, nodes.Const) for arg in node.args): |
168 |
return False |
169 |
|
170 |
func = node.func |
171 |
if isinstance(func, nodes.Attribute): |
172 |
if func.attrname != 'require_version': |
173 |
return False |
174 |
if isinstance(func.expr, nodes.Name) and func.expr.name == 'gi': |
175 |
return True |
176 |
|
177 |
return False |
178 |
|
179 |
if isinstance(func, nodes.Name): |
180 |
return func.name == 'require_version' |
181 |
|
182 |
return False |
183 |
|
184 |
def _register_require_version(node): |
185 |
# Load the gi.require_version locally
|
186 |
try:
|
187 |
import gi |
188 |
gi.require_version(node.args[0].value, node.args[1].value) |
189 |
except Exception: |
190 |
pass
|
191 |
|
192 |
return node
|
193 |
|
194 |
MANAGER.register_failed_import_hook(_import_gi_module) |
195 |
MANAGER.register_transform(nodes.Call, _register_require_version, _looks_like_require_version) |