Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

brain_gi.py 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. # Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
  2. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  3. # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
  4. """Astroid hooks for the Python 2 GObject introspection bindings.
  5. Helps with understanding everything imported from 'gi.repository'
  6. """
  7. import inspect
  8. import itertools
  9. import sys
  10. import re
  11. import warnings
  12. from astroid import MANAGER, AstroidBuildingError, nodes
  13. from astroid.builder import AstroidBuilder
  14. _inspected_modules = {}
  15. _identifier_re = r'^[A-Za-z_]\w*$'
  16. def _gi_build_stub(parent):
  17. """
  18. Inspect the passed module recursively and build stubs for functions,
  19. classes, etc.
  20. """
  21. classes = {}
  22. functions = {}
  23. constants = {}
  24. methods = {}
  25. for name in dir(parent):
  26. if name.startswith("__"):
  27. continue
  28. # Check if this is a valid name in python
  29. if not re.match(_identifier_re, name):
  30. continue
  31. try:
  32. obj = getattr(parent, name)
  33. except:
  34. continue
  35. if inspect.isclass(obj):
  36. classes[name] = obj
  37. elif (inspect.isfunction(obj) or
  38. inspect.isbuiltin(obj)):
  39. functions[name] = obj
  40. elif (inspect.ismethod(obj) or
  41. inspect.ismethoddescriptor(obj)):
  42. methods[name] = obj
  43. elif (str(obj).startswith("<flags") or
  44. str(obj).startswith("<enum ") or
  45. str(obj).startswith("<GType ") or
  46. inspect.isdatadescriptor(obj)):
  47. constants[name] = 0
  48. elif isinstance(obj, (int, str)):
  49. constants[name] = obj
  50. elif callable(obj):
  51. # Fall back to a function for anything callable
  52. functions[name] = obj
  53. else:
  54. # Assume everything else is some manner of constant
  55. constants[name] = 0
  56. ret = ""
  57. if constants:
  58. ret += "# %s constants\n\n" % parent.__name__
  59. for name in sorted(constants):
  60. if name[0].isdigit():
  61. # GDK has some busted constant names like
  62. # Gdk.EventType.2BUTTON_PRESS
  63. continue
  64. val = constants[name]
  65. strval = str(val)
  66. if isinstance(val, str):
  67. strval = '"%s"' % str(val).replace("\\", "\\\\")
  68. ret += "%s = %s\n" % (name, strval)
  69. if ret:
  70. ret += "\n\n"
  71. if functions:
  72. ret += "# %s functions\n\n" % parent.__name__
  73. for name in sorted(functions):
  74. ret += "def %s(*args, **kwargs):\n" % name
  75. ret += " pass\n"
  76. if ret:
  77. ret += "\n\n"
  78. if methods:
  79. ret += "# %s methods\n\n" % parent.__name__
  80. for name in sorted(methods):
  81. ret += "def %s(self, *args, **kwargs):\n" % name
  82. ret += " pass\n"
  83. if ret:
  84. ret += "\n\n"
  85. if classes:
  86. ret += "# %s classes\n\n" % parent.__name__
  87. for name in sorted(classes):
  88. ret += "class %s(object):\n" % name
  89. classret = _gi_build_stub(classes[name])
  90. if not classret:
  91. classret = "pass\n"
  92. for line in classret.splitlines():
  93. ret += " " + line + "\n"
  94. ret += "\n"
  95. return ret
  96. def _import_gi_module(modname):
  97. # we only consider gi.repository submodules
  98. if not modname.startswith('gi.repository.'):
  99. raise AstroidBuildingError(modname=modname)
  100. # build astroid representation unless we already tried so
  101. if modname not in _inspected_modules:
  102. modnames = [modname]
  103. optional_modnames = []
  104. # GLib and GObject may have some special case handling
  105. # in pygobject that we need to cope with. However at
  106. # least as of pygobject3-3.13.91 the _glib module doesn't
  107. # exist anymore, so if treat these modules as optional.
  108. if modname == 'gi.repository.GLib':
  109. optional_modnames.append('gi._glib')
  110. elif modname == 'gi.repository.GObject':
  111. optional_modnames.append('gi._gobject')
  112. try:
  113. modcode = ''
  114. for m in itertools.chain(modnames, optional_modnames):
  115. try:
  116. with warnings.catch_warnings():
  117. # Just inspecting the code can raise gi deprecation
  118. # warnings, so ignore them.
  119. try:
  120. from gi import PyGIDeprecationWarning, PyGIWarning
  121. warnings.simplefilter("ignore", PyGIDeprecationWarning)
  122. warnings.simplefilter("ignore", PyGIWarning)
  123. except Exception:
  124. pass
  125. __import__(m)
  126. modcode += _gi_build_stub(sys.modules[m])
  127. except ImportError:
  128. if m not in optional_modnames:
  129. raise
  130. except ImportError:
  131. astng = _inspected_modules[modname] = None
  132. else:
  133. astng = AstroidBuilder(MANAGER).string_build(modcode, modname)
  134. _inspected_modules[modname] = astng
  135. else:
  136. astng = _inspected_modules[modname]
  137. if astng is None:
  138. raise AstroidBuildingError(modname=modname)
  139. return astng
  140. def _looks_like_require_version(node):
  141. # Return whether this looks like a call to gi.require_version(<name>, <version>)
  142. # Only accept function calls with two constant arguments
  143. if len(node.args) != 2:
  144. return False
  145. if not all(isinstance(arg, nodes.Const) for arg in node.args):
  146. return False
  147. func = node.func
  148. if isinstance(func, nodes.Attribute):
  149. if func.attrname != 'require_version':
  150. return False
  151. if isinstance(func.expr, nodes.Name) and func.expr.name == 'gi':
  152. return True
  153. return False
  154. if isinstance(func, nodes.Name):
  155. return func.name == 'require_version'
  156. return False
  157. def _register_require_version(node):
  158. # Load the gi.require_version locally
  159. try:
  160. import gi
  161. gi.require_version(node.args[0].value, node.args[1].value)
  162. except Exception:
  163. pass
  164. return node
  165. MANAGER.register_failed_import_hook(_import_gi_module)
  166. MANAGER.register_transform(nodes.Call, _register_require_version, _looks_like_require_version)