| Index: third_party/logilab/logilab/astroid/brain/py2gi.py
|
| diff --git a/third_party/logilab/logilab/astroid/brain/py2gi.py b/third_party/logilab/logilab/astroid/brain/py2gi.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6747898de5b8172e87f06edb4e62a82d7ac58a01
|
| --- /dev/null
|
| +++ b/third_party/logilab/logilab/astroid/brain/py2gi.py
|
| @@ -0,0 +1,155 @@
|
| +"""Astroid hooks for the Python 2 GObject introspection bindings.
|
| +
|
| +Helps with understanding everything imported from 'gi.repository'
|
| +"""
|
| +
|
| +import inspect
|
| +import itertools
|
| +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
|
| +
|
| +def _import_gi_module(modname):
|
| + # we only consider gi.repository submodules
|
| + if not modname.startswith('gi.repository.'):
|
| + raise AstroidBuildingException()
|
| + # build astroid representation unless we already tried so
|
| + if modname not in _inspected_modules:
|
| + modnames = [modname]
|
| + optional_modnames = []
|
| +
|
| + # GLib and GObject may have some special case handling
|
| + # in pygobject that we need to cope with. However at
|
| + # least as of pygobject3-3.13.91 the _glib module doesn't
|
| + # exist anymore, so if treat these modules as optional.
|
| + if modname == 'gi.repository.GLib':
|
| + optional_modnames.append('gi._glib')
|
| + elif modname == 'gi.repository.GObject':
|
| + optional_modnames.append('gi._gobject')
|
| +
|
| + try:
|
| + modcode = ''
|
| + for m in itertools.chain(modnames, optional_modnames):
|
| + try:
|
| + __import__(m)
|
| + modcode += _gi_build_stub(sys.modules[m])
|
| + except ImportError:
|
| + if m not in optional_modnames:
|
| + raise
|
| + 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
|
| +
|
| +
|
| +MANAGER.register_failed_import_hook(_import_gi_module)
|
| +
|
|
|