Index: third_party/logilab/astroid/brain/py2gi.py |
=================================================================== |
--- third_party/logilab/astroid/brain/py2gi.py (revision 0) |
+++ third_party/logilab/astroid/brain/py2gi.py (working copy) |
@@ -0,0 +1,159 @@ |
+"""Astroid hooks for the Python 2 GObject introspection bindings. |
+ |
+Helps with understanding everything imported from 'gi.repository' |
+""" |
+ |
+import inspect |
+import sys |
+import re |
+ |
+from astroid import MANAGER, AstroidBuildingException |
+from astroid.builder import AstroidBuilder |
+ |
+ |
+_inspected_modules = {} |
+ |
+_identifier_re = r'^[A-Za-z_]\w*$' |
+ |
+def _gi_build_stub(parent): |
+ """ |
+ Inspect the passed module recursively and build stubs for functions, |
+ classes, etc. |
+ """ |
+ classes = {} |
+ functions = {} |
+ constants = {} |
+ methods = {} |
+ for name in dir(parent): |
+ if name.startswith("__"): |
+ continue |
+ |
+ # Check if this is a valid name in python |
+ if not re.match(_identifier_re, name): |
+ continue |
+ |
+ try: |
+ obj = getattr(parent, name) |
+ except: |
+ continue |
+ |
+ if inspect.isclass(obj): |
+ classes[name] = obj |
+ elif (inspect.isfunction(obj) or |
+ inspect.isbuiltin(obj)): |
+ functions[name] = obj |
+ elif (inspect.ismethod(obj) or |
+ inspect.ismethoddescriptor(obj)): |
+ methods[name] = obj |
+ elif type(obj) in [int, str]: |
+ constants[name] = obj |
+ elif (str(obj).startswith("<flags") or |
+ str(obj).startswith("<enum ") or |
+ str(obj).startswith("<GType ") or |
+ inspect.isdatadescriptor(obj)): |
+ constants[name] = 0 |
+ elif callable(obj): |
+ # Fall back to a function for anything callable |
+ functions[name] = obj |
+ else: |
+ # Assume everything else is some manner of constant |
+ constants[name] = 0 |
+ |
+ ret = "" |
+ |
+ if constants: |
+ ret += "# %s contants\n\n" % parent.__name__ |
+ for name in sorted(constants): |
+ if name[0].isdigit(): |
+ # GDK has some busted constant names like |
+ # Gdk.EventType.2BUTTON_PRESS |
+ continue |
+ |
+ val = constants[name] |
+ |
+ strval = str(val) |
+ if type(val) is str: |
+ strval = '"%s"' % str(val).replace("\\", "\\\\") |
+ ret += "%s = %s\n" % (name, strval) |
+ |
+ if ret: |
+ ret += "\n\n" |
+ if functions: |
+ ret += "# %s functions\n\n" % parent.__name__ |
+ for name in sorted(functions): |
+ func = functions[name] |
+ ret += "def %s(*args, **kwargs):\n" % name |
+ ret += " pass\n" |
+ |
+ if ret: |
+ ret += "\n\n" |
+ if methods: |
+ ret += "# %s methods\n\n" % parent.__name__ |
+ for name in sorted(methods): |
+ func = methods[name] |
+ ret += "def %s(self, *args, **kwargs):\n" % name |
+ ret += " pass\n" |
+ |
+ if ret: |
+ ret += "\n\n" |
+ if classes: |
+ ret += "# %s classes\n\n" % parent.__name__ |
+ for name in sorted(classes): |
+ ret += "class %s(object):\n" % name |
+ |
+ classret = _gi_build_stub(classes[name]) |
+ if not classret: |
+ classret = "pass\n" |
+ |
+ for line in classret.splitlines(): |
+ ret += " " + line + "\n" |
+ ret += "\n" |
+ |
+ return ret |
+ |
+# Overwrite Module.module_import to _actually_ import the introspected module if |
+# it's a gi module, then build stub code by examining its info and get an astng |
+# from that |
+ |
+from astroid.scoped_nodes import Module |
+_orig_import_module = Module.import_module |
+ |
+def _new_import_module(self, modname, relative_only=False, level=None): |
+ # Could be a static piece of gi.repository or whatever unrelated module, |
+ # let that fall through |
+ try: |
+ return _orig_import_module(self, modname, relative_only, level) |
+ except AstroidBuildingException: |
+ # we only consider gi.repository submodules |
+ if not modname.startswith('gi.repository.'): |
+ if relative_only and level is None: |
+ level = 0 |
+ modname = self.relative_to_absolute_name(modname, level) |
+ if not modname.startswith('gi.repository.'): |
+ raise |
+ # build astroid representation unless we already tried so |
+ if modname not in _inspected_modules: |
+ modnames = [modname] |
+ # GLib and GObject have some special case handling |
+ # in pygobject that we need to cope with |
+ if modname == 'gi.repository.GLib': |
+ modnames.append('gi._glib') |
+ elif modname == 'gi.repository.GObject': |
+ modnames.append('gi._gobject') |
+ try: |
+ modcode = '' |
+ for m in modnames: |
+ __import__(m) |
+ modcode += _gi_build_stub(sys.modules[m]) |
+ except ImportError: |
+ astng = _inspected_modules[modname] = None |
+ else: |
+ astng = AstroidBuilder(MANAGER).string_build(modcode, modname) |
+ _inspected_modules[modname] = astng |
+ else: |
+ astng = _inspected_modules[modname] |
+ if astng is None: |
+ raise AstroidBuildingException('Failed to import module %r' % modname) |
+ return astng |
+ |
+Module.import_module = _new_import_module |