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

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

91 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 class Moves(object): 

112 {} 

113 moves = Moves() 

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

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

116 module.name = "six.moves" 

117 return module 

118 

119 

120def _six_fail_hook(modname): 

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

122 class. 

123 

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

125 for six 

126 

127 :param modname: Name of failed module 

128 :type modname: str 

129 

130 :return: An astroid module 

131 :rtype: nodes.Module 

132 """ 

133 

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

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

136 raise AstroidBuildingError(modname=modname) 

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

138 module.name = "six.moves" 

139 if attribute_of: 

140 # Facilitate import of submodules in Moves 

141 start_index = len(module.name) 

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

143 try: 

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

145 except AttributeInferenceError as exc: 

146 raise AstroidBuildingError(modname=modname) from exc 

147 if isinstance(import_attr, nodes.Import): 

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

149 return submodule 

150 # Let dummy submodule imports pass through 

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

152 return module 

153 

154 

155def _looks_like_decorated_with_six_add_metaclass(node) -> bool: 

156 if not node.decorators: 

157 return False 

158 

159 for decorator in node.decorators.nodes: 

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

161 continue 

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

163 return True 

164 return False 

165 

166 

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

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

169 

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

171 """ 

172 if not node.decorators: 

173 return 

174 

175 for decorator in node.decorators.nodes: 

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

177 continue 

178 

179 try: 

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

181 except (InferenceError, StopIteration): 

182 continue 

183 if ( 

184 isinstance(func, (nodes.FunctionDef, nodes.ClassDef)) 

185 and func.qname() == SIX_ADD_METACLASS 

186 and decorator.args 

187 ): 

188 metaclass = decorator.args[0] 

189 node._metaclass = metaclass 

190 return node 

191 return 

192 

193 

194def _looks_like_nested_from_six_with_metaclass(node) -> bool: 

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

196 return False 

197 base = node.bases[0] 

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

199 return False 

200 try: 

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

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

203 mod = base.func.expr.name 

204 func = base.func.attrname 

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

206 else: 

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

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

209 mod = base.parent.parent 

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

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

212 except (AttributeError, KeyError, IndexError): 

213 return False 

214 return func == SIX_WITH_METACLASS 

215 

216 

217def transform_six_with_metaclass(node): 

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

219 

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

221 """ 

222 call = node.bases[0] 

223 node._metaclass = call.args[0] 

224 return node 

225 

226 

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

228 register_module_extender(manager, "six", six_moves_transform) 

229 register_module_extender( 

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

231 ) 

232 manager.register_failed_import_hook(_six_fail_hook) 

233 manager.register_transform( 

234 nodes.ClassDef, 

235 transform_six_add_metaclass, 

236 _looks_like_decorated_with_six_add_metaclass, 

237 ) 

238 manager.register_transform( 

239 nodes.ClassDef, 

240 transform_six_with_metaclass, 

241 _looks_like_nested_from_six_with_metaclass, 

242 )