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 |