Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/brain/brain_gi.py: 17%

139 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:53 +0000

1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html 

2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE 

3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt 

4 

5"""Astroid hooks for the Python 2 GObject introspection bindings. 

6 

7Helps with understanding everything imported from 'gi.repository' 

8""" 

9 

10# pylint:disable=import-error,import-outside-toplevel 

11 

12import inspect 

13import itertools 

14import re 

15import sys 

16import warnings 

17 

18from astroid import nodes 

19from astroid.builder import AstroidBuilder 

20from astroid.exceptions import AstroidBuildingError 

21from astroid.manager import AstroidManager 

22 

23_inspected_modules = {} 

24 

25_identifier_re = r"^[A-Za-z_]\w*$" 

26 

27_special_methods = frozenset( 

28 { 

29 "__lt__", 

30 "__le__", 

31 "__eq__", 

32 "__ne__", 

33 "__ge__", 

34 "__gt__", 

35 "__iter__", 

36 "__getitem__", 

37 "__setitem__", 

38 "__delitem__", 

39 "__len__", 

40 "__bool__", 

41 "__nonzero__", 

42 "__next__", 

43 "__str__", 

44 "__contains__", 

45 "__enter__", 

46 "__exit__", 

47 "__repr__", 

48 "__getattr__", 

49 "__setattr__", 

50 "__delattr__", 

51 "__del__", 

52 "__hash__", 

53 } 

54) 

55 

56 

57def _gi_build_stub(parent): # noqa: C901 

58 """ 

59 Inspect the passed module recursively and build stubs for functions, 

60 classes, etc. 

61 """ 

62 classes = {} 

63 functions = {} 

64 constants = {} 

65 methods = {} 

66 for name in dir(parent): 

67 if name.startswith("__") and name not in _special_methods: 

68 continue 

69 

70 # Check if this is a valid name in python 

71 if not re.match(_identifier_re, name): 

72 continue 

73 

74 try: 

75 obj = getattr(parent, name) 

76 except Exception: # pylint: disable=broad-except 

77 # gi.module.IntrospectionModule.__getattr__() can raise all kinds of things 

78 # like ValueError, TypeError, NotImplementedError, RepositoryError, etc 

79 continue 

80 

81 if inspect.isclass(obj): 

82 classes[name] = obj 

83 elif inspect.isfunction(obj) or inspect.isbuiltin(obj): 

84 functions[name] = obj 

85 elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): 

86 methods[name] = obj 

87 elif ( 

88 str(obj).startswith("<flags") 

89 or str(obj).startswith("<enum ") 

90 or str(obj).startswith("<GType ") 

91 or inspect.isdatadescriptor(obj) 

92 ): 

93 constants[name] = 0 

94 elif isinstance(obj, (int, str)): 

95 constants[name] = obj 

96 elif callable(obj): 

97 # Fall back to a function for anything callable 

98 functions[name] = obj 

99 else: 

100 # Assume everything else is some manner of constant 

101 constants[name] = 0 

102 

103 ret = "" 

104 

105 if constants: 

106 ret += f"# {parent.__name__} constants\n\n" 

107 for name in sorted(constants): 

108 if name[0].isdigit(): 

109 # GDK has some busted constant names like 

110 # Gdk.EventType.2BUTTON_PRESS 

111 continue 

112 

113 val = constants[name] 

114 

115 strval = str(val) 

116 if isinstance(val, str): 

117 strval = '"%s"' % str(val).replace("\\", "\\\\") 

118 ret += f"{name} = {strval}\n" 

119 

120 if ret: 

121 ret += "\n\n" 

122 if functions: 

123 ret += f"# {parent.__name__} functions\n\n" 

124 for name in sorted(functions): 

125 ret += f"def {name}(*args, **kwargs):\n" 

126 ret += " pass\n" 

127 

128 if ret: 

129 ret += "\n\n" 

130 if methods: 

131 ret += f"# {parent.__name__} methods\n\n" 

132 for name in sorted(methods): 

133 ret += f"def {name}(self, *args, **kwargs):\n" 

134 ret += " pass\n" 

135 

136 if ret: 

137 ret += "\n\n" 

138 if classes: 

139 ret += f"# {parent.__name__} classes\n\n" 

140 for name, obj in sorted(classes.items()): 

141 base = "object" 

142 if issubclass(obj, Exception): 

143 base = "Exception" 

144 ret += f"class {name}({base}):\n" 

145 

146 classret = _gi_build_stub(obj) 

147 if not classret: 

148 classret = "pass\n" 

149 

150 for line in classret.splitlines(): 

151 ret += " " + line + "\n" 

152 ret += "\n" 

153 

154 return ret 

155 

156 

157def _import_gi_module(modname): 

158 # we only consider gi.repository submodules 

159 if not modname.startswith("gi.repository."): 

160 raise AstroidBuildingError(modname=modname) 

161 # build astroid representation unless we already tried so 

162 if modname not in _inspected_modules: 

163 modnames = [modname] 

164 optional_modnames = [] 

165 

166 # GLib and GObject may have some special case handling 

167 # in pygobject that we need to cope with. However at 

168 # least as of pygobject3-3.13.91 the _glib module doesn't 

169 # exist anymore, so if treat these modules as optional. 

170 if modname == "gi.repository.GLib": 

171 optional_modnames.append("gi._glib") 

172 elif modname == "gi.repository.GObject": 

173 optional_modnames.append("gi._gobject") 

174 

175 try: 

176 modcode = "" 

177 for m in itertools.chain(modnames, optional_modnames): 

178 try: 

179 with warnings.catch_warnings(): 

180 # Just inspecting the code can raise gi deprecation 

181 # warnings, so ignore them. 

182 try: 

183 from gi import ( # pylint:disable=import-error 

184 PyGIDeprecationWarning, 

185 PyGIWarning, 

186 ) 

187 

188 warnings.simplefilter("ignore", PyGIDeprecationWarning) 

189 warnings.simplefilter("ignore", PyGIWarning) 

190 except Exception: # pylint:disable=broad-except 

191 pass 

192 

193 __import__(m) 

194 modcode += _gi_build_stub(sys.modules[m]) 

195 except ImportError: 

196 if m not in optional_modnames: 

197 raise 

198 except ImportError: 

199 astng = _inspected_modules[modname] = None 

200 else: 

201 astng = AstroidBuilder(AstroidManager()).string_build(modcode, modname) 

202 _inspected_modules[modname] = astng 

203 else: 

204 astng = _inspected_modules[modname] 

205 if astng is None: 

206 raise AstroidBuildingError(modname=modname) 

207 return astng 

208 

209 

210def _looks_like_require_version(node) -> bool: 

211 # Return whether this looks like a call to gi.require_version(<name>, <version>) 

212 # Only accept function calls with two constant arguments 

213 if len(node.args) != 2: 

214 return False 

215 

216 if not all(isinstance(arg, nodes.Const) for arg in node.args): 

217 return False 

218 

219 func = node.func 

220 if isinstance(func, nodes.Attribute): 

221 if func.attrname != "require_version": 

222 return False 

223 if isinstance(func.expr, nodes.Name) and func.expr.name == "gi": 

224 return True 

225 

226 return False 

227 

228 if isinstance(func, nodes.Name): 

229 return func.name == "require_version" 

230 

231 return False 

232 

233 

234def _register_require_version(node): 

235 # Load the gi.require_version locally 

236 try: 

237 import gi 

238 

239 gi.require_version(node.args[0].value, node.args[1].value) 

240 except Exception: # pylint:disable=broad-except 

241 pass 

242 

243 return node 

244 

245 

246AstroidManager().register_failed_import_hook(_import_gi_module) 

247AstroidManager().register_transform( 

248 nodes.Call, _register_require_version, _looks_like_require_version 

249)