Statistics
| Revision:

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)