Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/arguments.py: 25%

220 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1import re 

2from itertools import zip_longest 

3 

4from parso.python import tree 

5 

6from jedi import debug 

7from jedi.inference.utils import PushBackIterator 

8from jedi.inference import analysis 

9from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \ 

10 LazyTreeValue, get_merged_lazy_value 

11from jedi.inference.names import ParamName, TreeNameDefinition, AnonymousParamName 

12from jedi.inference.base_value import NO_VALUES, ValueSet, ContextualizedNode 

13from jedi.inference.value import iterable 

14from jedi.inference.cache import inference_state_as_method_param_cache 

15 

16 

17def try_iter_content(types, depth=0): 

18 """Helper method for static analysis.""" 

19 if depth > 10: 

20 # It's possible that a loop has references on itself (especially with 

21 # CompiledValue). Therefore don't loop infinitely. 

22 return 

23 

24 for typ in types: 

25 try: 

26 f = typ.py__iter__ 

27 except AttributeError: 

28 pass 

29 else: 

30 for lazy_value in f(): 

31 try_iter_content(lazy_value.infer(), depth + 1) 

32 

33 

34class ParamIssue(Exception): 

35 pass 

36 

37 

38def repack_with_argument_clinic(clinic_string): 

39 """ 

40 Transforms a function or method with arguments to the signature that is 

41 given as an argument clinic notation. 

42 

43 Argument clinic is part of CPython and used for all the functions that are 

44 implemented in C (Python 3.7): 

45 

46 str.split.__text_signature__ 

47 # Results in: '($self, /, sep=None, maxsplit=-1)' 

48 """ 

49 def decorator(func): 

50 def wrapper(value, arguments): 

51 try: 

52 args = tuple(iterate_argument_clinic( 

53 value.inference_state, 

54 arguments, 

55 clinic_string, 

56 )) 

57 except ParamIssue: 

58 return NO_VALUES 

59 else: 

60 return func(value, *args) 

61 

62 return wrapper 

63 return decorator 

64 

65 

66def iterate_argument_clinic(inference_state, arguments, clinic_string): 

67 """Uses a list with argument clinic information (see PEP 436).""" 

68 clinic_args = list(_parse_argument_clinic(clinic_string)) 

69 

70 iterator = PushBackIterator(arguments.unpack()) 

71 for i, (name, optional, allow_kwargs, stars) in enumerate(clinic_args): 

72 if stars == 1: 

73 lazy_values = [] 

74 for key, argument in iterator: 

75 if key is not None: 

76 iterator.push_back((key, argument)) 

77 break 

78 

79 lazy_values.append(argument) 

80 yield ValueSet([iterable.FakeTuple(inference_state, lazy_values)]) 

81 lazy_values 

82 continue 

83 elif stars == 2: 

84 raise NotImplementedError() 

85 key, argument = next(iterator, (None, None)) 

86 if key is not None: 

87 debug.warning('Keyword arguments in argument clinic are currently not supported.') 

88 raise ParamIssue 

89 if argument is None and not optional: 

90 debug.warning('TypeError: %s expected at least %s arguments, got %s', 

91 name, len(clinic_args), i) 

92 raise ParamIssue 

93 

94 value_set = NO_VALUES if argument is None else argument.infer() 

95 

96 if not value_set and not optional: 

97 # For the stdlib we always want values. If we don't get them, 

98 # that's ok, maybe something is too hard to resolve, however, 

99 # we will not proceed with the type inference of that function. 

100 debug.warning('argument_clinic "%s" not resolvable.', name) 

101 raise ParamIssue 

102 yield value_set 

103 

104 

105def _parse_argument_clinic(string): 

106 allow_kwargs = False 

107 optional = False 

108 while string: 

109 # Optional arguments have to begin with a bracket. And should always be 

110 # at the end of the arguments. This is therefore not a proper argument 

111 # clinic implementation. `range()` for exmple allows an optional start 

112 # value at the beginning. 

113 match = re.match(r'(?:(?:(\[),? ?|, ?|)(\**\w+)|, ?/)\]*', string) 

114 string = string[len(match.group(0)):] 

115 if not match.group(2): # A slash -> allow named arguments 

116 allow_kwargs = True 

117 continue 

118 optional = optional or bool(match.group(1)) 

119 word = match.group(2) 

120 stars = word.count('*') 

121 word = word[stars:] 

122 yield (word, optional, allow_kwargs, stars) 

123 if stars: 

124 allow_kwargs = True 

125 

126 

127class _AbstractArgumentsMixin: 

128 def unpack(self, funcdef=None): 

129 raise NotImplementedError 

130 

131 def get_calling_nodes(self): 

132 return [] 

133 

134 

135class AbstractArguments(_AbstractArgumentsMixin): 

136 context = None 

137 argument_node = None 

138 trailer = None 

139 

140 

141def unpack_arglist(arglist): 

142 if arglist is None: 

143 return 

144 

145 if arglist.type != 'arglist' and not ( 

146 arglist.type == 'argument' and arglist.children[0] in ('*', '**')): 

147 yield 0, arglist 

148 return 

149 

150 iterator = iter(arglist.children) 

151 for child in iterator: 

152 if child == ',': 

153 continue 

154 elif child in ('*', '**'): 

155 c = next(iterator, None) 

156 assert c is not None 

157 yield len(child.value), c 

158 elif child.type == 'argument' and \ 

159 child.children[0] in ('*', '**'): 

160 assert len(child.children) == 2 

161 yield len(child.children[0].value), child.children[1] 

162 else: 

163 yield 0, child 

164 

165 

166class TreeArguments(AbstractArguments): 

167 def __init__(self, inference_state, context, argument_node, trailer=None): 

168 """ 

169 :param argument_node: May be an argument_node or a list of nodes. 

170 """ 

171 self.argument_node = argument_node 

172 self.context = context 

173 self._inference_state = inference_state 

174 self.trailer = trailer # Can be None, e.g. in a class definition. 

175 

176 @classmethod 

177 @inference_state_as_method_param_cache() 

178 def create_cached(cls, *args, **kwargs): 

179 return cls(*args, **kwargs) 

180 

181 def unpack(self, funcdef=None): 

182 named_args = [] 

183 for star_count, el in unpack_arglist(self.argument_node): 

184 if star_count == 1: 

185 arrays = self.context.infer_node(el) 

186 iterators = [_iterate_star_args(self.context, a, el, funcdef) 

187 for a in arrays] 

188 for values in list(zip_longest(*iterators)): 

189 yield None, get_merged_lazy_value( 

190 [v for v in values if v is not None] 

191 ) 

192 elif star_count == 2: 

193 arrays = self.context.infer_node(el) 

194 for dct in arrays: 

195 yield from _star_star_dict(self.context, dct, el, funcdef) 

196 else: 

197 if el.type == 'argument': 

198 c = el.children 

199 if len(c) == 3: # Keyword argument. 

200 named_args.append((c[0].value, LazyTreeValue(self.context, c[2]),)) 

201 else: # Generator comprehension. 

202 # Include the brackets with the parent. 

203 sync_comp_for = el.children[1] 

204 if sync_comp_for.type == 'comp_for': 

205 sync_comp_for = sync_comp_for.children[1] 

206 comp = iterable.GeneratorComprehension( 

207 self._inference_state, 

208 defining_context=self.context, 

209 sync_comp_for_node=sync_comp_for, 

210 entry_node=el.children[0], 

211 ) 

212 yield None, LazyKnownValue(comp) 

213 else: 

214 yield None, LazyTreeValue(self.context, el) 

215 

216 # Reordering arguments is necessary, because star args sometimes appear 

217 # after named argument, but in the actual order it's prepended. 

218 yield from named_args 

219 

220 def _as_tree_tuple_objects(self): 

221 for star_count, argument in unpack_arglist(self.argument_node): 

222 default = None 

223 if argument.type == 'argument': 

224 if len(argument.children) == 3: # Keyword argument. 

225 argument, default = argument.children[::2] 

226 yield argument, default, star_count 

227 

228 def iter_calling_names_with_star(self): 

229 for name, default, star_count in self._as_tree_tuple_objects(): 

230 # TODO this function is a bit strange. probably refactor? 

231 if not star_count or not isinstance(name, tree.Name): 

232 continue 

233 

234 yield TreeNameDefinition(self.context, name) 

235 

236 def __repr__(self): 

237 return '<%s: %s>' % (self.__class__.__name__, self.argument_node) 

238 

239 def get_calling_nodes(self): 

240 old_arguments_list = [] 

241 arguments = self 

242 

243 while arguments not in old_arguments_list: 

244 if not isinstance(arguments, TreeArguments): 

245 break 

246 

247 old_arguments_list.append(arguments) 

248 for calling_name in reversed(list(arguments.iter_calling_names_with_star())): 

249 names = calling_name.goto() 

250 if len(names) != 1: 

251 break 

252 if isinstance(names[0], AnonymousParamName): 

253 # Dynamic parameters should not have calling nodes, because 

254 # they are dynamic and extremely random. 

255 return [] 

256 if not isinstance(names[0], ParamName): 

257 break 

258 executed_param_name = names[0].get_executed_param_name() 

259 arguments = executed_param_name.arguments 

260 break 

261 

262 if arguments.argument_node is not None: 

263 return [ContextualizedNode(arguments.context, arguments.argument_node)] 

264 if arguments.trailer is not None: 

265 return [ContextualizedNode(arguments.context, arguments.trailer)] 

266 return [] 

267 

268 

269class ValuesArguments(AbstractArguments): 

270 def __init__(self, values_list): 

271 self._values_list = values_list 

272 

273 def unpack(self, funcdef=None): 

274 for values in self._values_list: 

275 yield None, LazyKnownValues(values) 

276 

277 def __repr__(self): 

278 return '<%s: %s>' % (self.__class__.__name__, self._values_list) 

279 

280 

281class TreeArgumentsWrapper(_AbstractArgumentsMixin): 

282 def __init__(self, arguments): 

283 self._wrapped_arguments = arguments 

284 

285 @property 

286 def context(self): 

287 return self._wrapped_arguments.context 

288 

289 @property 

290 def argument_node(self): 

291 return self._wrapped_arguments.argument_node 

292 

293 @property 

294 def trailer(self): 

295 return self._wrapped_arguments.trailer 

296 

297 def unpack(self, func=None): 

298 raise NotImplementedError 

299 

300 def get_calling_nodes(self): 

301 return self._wrapped_arguments.get_calling_nodes() 

302 

303 def __repr__(self): 

304 return '<%s: %s>' % (self.__class__.__name__, self._wrapped_arguments) 

305 

306 

307def _iterate_star_args(context, array, input_node, funcdef=None): 

308 if not array.py__getattribute__('__iter__'): 

309 if funcdef is not None: 

310 # TODO this funcdef should not be needed. 

311 m = "TypeError: %s() argument after * must be a sequence, not %s" \ 

312 % (funcdef.name.value, array) 

313 analysis.add(context, 'type-error-star', input_node, message=m) 

314 try: 

315 iter_ = array.py__iter__ 

316 except AttributeError: 

317 pass 

318 else: 

319 yield from iter_() 

320 

321 

322def _star_star_dict(context, array, input_node, funcdef): 

323 from jedi.inference.value.instance import CompiledInstance 

324 if isinstance(array, CompiledInstance) and array.name.string_name == 'dict': 

325 # For now ignore this case. In the future add proper iterators and just 

326 # make one call without crazy isinstance checks. 

327 return {} 

328 elif isinstance(array, iterable.Sequence) and array.array_type == 'dict': 

329 return array.exact_key_items() 

330 else: 

331 if funcdef is not None: 

332 m = "TypeError: %s argument after ** must be a mapping, not %s" \ 

333 % (funcdef.name.value, array) 

334 analysis.add(context, 'type-error-star-star', input_node, message=m) 

335 return {}