Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/astroid/brain/brain_six.py: 51%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

90 statements  

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 six module.""" 

6 

7from textwrap import dedent 

8 

9from astroid import nodes 

10from astroid.brain.helpers import register_module_extender 

11from astroid.builder import AstroidBuilder 

12from astroid.exceptions import ( 

13 AstroidBuildingError, 

14 AttributeInferenceError, 

15 InferenceError, 

16) 

17from astroid.manager import AstroidManager 

18 

19SIX_ADD_METACLASS = "six.add_metaclass" 

20SIX_WITH_METACLASS = "six.with_metaclass" 

21 

22 

23def default_predicate(line): 

24 return line.strip() 

25 

26 

27def _indent(text, prefix, predicate=default_predicate) -> str: 

28 """Adds 'prefix' to the beginning of selected lines in 'text'. 

29 

30 If 'predicate' is provided, 'prefix' will only be added to the lines 

31 where 'predicate(line)' is True. If 'predicate' is not provided, 

32 it will default to adding 'prefix' to all non-empty lines that do not 

33 consist solely of whitespace characters. 

34 """ 

35 

36 def prefixed_lines(): 

37 for line in text.splitlines(True): 

38 yield prefix + line if predicate(line) else line 

39 

40 return "".join(prefixed_lines()) 

41 

42 

43_IMPORTS = """ 

44import _io 

45cStringIO = _io.StringIO 

46filter = filter 

47from itertools import filterfalse 

48input = input 

49from sys import intern 

50map = map 

51range = range 

52from importlib import reload 

53reload_module = lambda module: reload(module) 

54from functools import reduce 

55from shlex import quote as shlex_quote 

56from io import StringIO 

57from collections import UserDict, UserList, UserString 

58xrange = range 

59zip = zip 

60from itertools import zip_longest 

61import builtins 

62import configparser 

63import copyreg 

64import _dummy_thread 

65import http.cookiejar as http_cookiejar 

66import http.cookies as http_cookies 

67import html.entities as html_entities 

68import html.parser as html_parser 

69import http.client as http_client 

70import http.server as http_server 

71BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server 

72import pickle as cPickle 

73import queue 

74import reprlib 

75import socketserver 

76import _thread 

77import winreg 

78import xmlrpc.server as xmlrpc_server 

79import xmlrpc.client as xmlrpc_client 

80import urllib.robotparser as urllib_robotparser 

81import email.mime.multipart as email_mime_multipart 

82import email.mime.nonmultipart as email_mime_nonmultipart 

83import email.mime.text as email_mime_text 

84import email.mime.base as email_mime_base 

85import urllib.parse as urllib_parse 

86import urllib.error as urllib_error 

87import tkinter 

88import tkinter.dialog as tkinter_dialog 

89import tkinter.filedialog as tkinter_filedialog 

90import tkinter.scrolledtext as tkinter_scrolledtext 

91import tkinter.simpledialog as tkinder_simpledialog 

92import tkinter.tix as tkinter_tix 

93import tkinter.ttk as tkinter_ttk 

94import tkinter.constants as tkinter_constants 

95import tkinter.dnd as tkinter_dnd 

96import tkinter.colorchooser as tkinter_colorchooser 

97import tkinter.commondialog as tkinter_commondialog 

98import tkinter.filedialog as tkinter_tkfiledialog 

99import tkinter.font as tkinter_font 

100import tkinter.messagebox as tkinter_messagebox 

101import urllib 

102import urllib.request as urllib_request 

103import urllib.robotparser as urllib_robotparser 

104import urllib.parse as urllib_parse 

105import urllib.error as urllib_error 

106""" 

107 

108 

109def six_moves_transform(): 

110 code = dedent( 

111 """ 

112 class Moves(object): 

113 {} 

114 moves = Moves() 

115 """ 

116 ).format(_indent(_IMPORTS, " ")) 

117 module = AstroidBuilder(AstroidManager()).string_build(code) 

118 module.name = "six.moves" 

119 return module 

120 

121 

122def _six_fail_hook(modname): 

123 """Fix six.moves imports due to the dynamic nature of this 

124 class. 

125 

126 Construct a pseudo-module which contains all the necessary imports 

127 for six 

128 

129 :param modname: Name of failed module 

130 :type modname: str 

131 

132 :return: An astroid module 

133 :rtype: nodes.Module 

134 """ 

135 

136 attribute_of = modname != "six.moves" and modname.startswith("six.moves") 

137 if modname != "six.moves" and not attribute_of: 

138 raise AstroidBuildingError(modname=modname) 

139 module = AstroidBuilder(AstroidManager()).string_build(_IMPORTS) 

140 module.name = "six.moves" 

141 if attribute_of: 

142 # Facilitate import of submodules in Moves 

143 start_index = len(module.name) 

144 attribute = modname[start_index:].lstrip(".").replace(".", "_") 

145 try: 

146 import_attr = module.getattr(attribute)[0] 

147 except AttributeInferenceError as exc: 

148 raise AstroidBuildingError(modname=modname) from exc 

149 if isinstance(import_attr, nodes.Import): 

150 submodule = AstroidManager().ast_from_module_name(import_attr.names[0][0]) 

151 return submodule 

152 # Let dummy submodule imports pass through 

153 # This will cause an Uninferable result, which is okay 

154 return module 

155 

156 

157def _looks_like_decorated_with_six_add_metaclass(node) -> bool: 

158 if not node.decorators: 

159 return False 

160 

161 for decorator in node.decorators.nodes: 

162 if not isinstance(decorator, nodes.Call): 

163 continue 

164 if decorator.func.as_string() == SIX_ADD_METACLASS: 

165 return True 

166 return False 

167 

168 

169def transform_six_add_metaclass(node): # pylint: disable=inconsistent-return-statements 

170 """Check if the given class node is decorated with *six.add_metaclass*. 

171 

172 If so, inject its argument as the metaclass of the underlying class. 

173 """ 

174 if not node.decorators: 

175 return 

176 

177 for decorator in node.decorators.nodes: 

178 if not isinstance(decorator, nodes.Call): 

179 continue 

180 

181 try: 

182 func = next(decorator.func.infer()) 

183 except (InferenceError, StopIteration): 

184 continue 

185 if func.qname() == SIX_ADD_METACLASS and decorator.args: 

186 metaclass = decorator.args[0] 

187 node._metaclass = metaclass 

188 return node 

189 return 

190 

191 

192def _looks_like_nested_from_six_with_metaclass(node) -> bool: 

193 if len(node.bases) != 1: 

194 return False 

195 base = node.bases[0] 

196 if not isinstance(base, nodes.Call): 

197 return False 

198 try: 

199 if hasattr(base.func, "expr"): 

200 # format when explicit 'six.with_metaclass' is used 

201 mod = base.func.expr.name 

202 func = base.func.attrname 

203 func = f"{mod}.{func}" 

204 else: 

205 # format when 'with_metaclass' is used directly (local import from six) 

206 # check reference module to avoid 'with_metaclass' name clashes 

207 mod = base.parent.parent 

208 import_from = mod.locals["with_metaclass"][0] 

209 func = f"{import_from.modname}.{base.func.name}" 

210 except (AttributeError, KeyError, IndexError): 

211 return False 

212 return func == SIX_WITH_METACLASS 

213 

214 

215def transform_six_with_metaclass(node): 

216 """Check if the given class node is defined with *six.with_metaclass*. 

217 

218 If so, inject its argument as the metaclass of the underlying class. 

219 """ 

220 call = node.bases[0] 

221 node._metaclass = call.args[0] 

222 return node 

223 

224 

225def register(manager: AstroidManager) -> None: 

226 register_module_extender(manager, "six", six_moves_transform) 

227 register_module_extender( 

228 manager, "requests.packages.urllib3.packages.six", six_moves_transform 

229 ) 

230 manager.register_failed_import_hook(_six_fail_hook) 

231 manager.register_transform( 

232 nodes.ClassDef, 

233 transform_six_add_metaclass, 

234 _looks_like_decorated_with_six_add_metaclass, 

235 ) 

236 manager.register_transform( 

237 nodes.ClassDef, 

238 transform_six_with_metaclass, 

239 _looks_like_nested_from_six_with_metaclass, 

240 )