| 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 |