Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/jedi-0.20.0-py3.11.egg/jedi/inference/arguments.py: 26%

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

222 statements  

1import re 

2from itertools import zip_longest 

3from typing import Any 

4 

5from parso.python import tree 

6 

7from jedi import debug 

8from jedi.inference.utils import PushBackIterator 

9from jedi.inference import analysis 

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

11 LazyTreeValue, get_merged_lazy_value 

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

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

14from jedi.inference.value import iterable 

15from jedi.inference.cache import inference_state_as_method_param_cache 

16 

17 

18def try_iter_content(types, depth=0): 

19 """Helper method for static analysis.""" 

20 if depth > 10: 

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

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

23 return 

24 

25 for typ in types: 

26 try: 

27 f = typ.py__iter__ 

28 except AttributeError: 

29 pass 

30 else: 

31 for lazy_value in f(): 

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

33 

34 

35class ParamIssue(Exception): 

36 pass 

37 

38 

39def repack_with_argument_clinic(clinic_string): 

40 """ 

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

42 given as an argument clinic notation. 

43 

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

45 implemented in C (Python 3.7): 

46 

47 str.split.__text_signature__ 

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

49 """ 

50 def decorator(func): 

51 def wrapper(value, arguments): 

52 try: 

53 args = tuple(iterate_argument_clinic( 

54 value.inference_state, 

55 arguments, 

56 clinic_string, 

57 )) 

58 except ParamIssue: 

59 return NO_VALUES 

60 else: 

61 return func(value, *args) 

62 

63 return wrapper 

64 return decorator 

65 

66 

67def iterate_argument_clinic(inference_state, arguments, clinic_string): 

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

69 clinic_args = list(_parse_argument_clinic(clinic_string)) 

70 

71 iterator = PushBackIterator(arguments.unpack()) 

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

73 if stars == 1: 

74 lazy_values = [] 

75 for key, argument in iterator: 

76 if key is not None: 

77 iterator.push_back((key, argument)) 

78 break 

79 

80 lazy_values.append(argument) 

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

82 lazy_values 

83 continue 

84 elif stars == 2: 

85 raise NotImplementedError() 

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

87 if key is not None: 

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

89 raise ParamIssue 

90 if argument is None and not optional: 

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

92 name, len(clinic_args), i) 

93 raise ParamIssue 

94 

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

96 

97 if not value_set and not optional: 

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

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

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

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

102 raise ParamIssue 

103 yield value_set 

104 

105 

106def _parse_argument_clinic(string): 

107 allow_kwargs = False 

108 optional = False 

109 while string: 

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

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

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

113 # value at the beginning. 

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

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

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

117 allow_kwargs = True 

118 continue 

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

120 word = match.group(2) 

121 stars = word.count('*') 

122 word = word[stars:] 

123 yield (word, optional, allow_kwargs, stars) 

124 if stars: 

125 allow_kwargs = True 

126 

127 

128class _AbstractArgumentsMixin: 

129 def unpack(self, funcdef=None): 

130 raise NotImplementedError 

131 

132 def get_calling_nodes(self): 

133 return [] 

134 

135 

136class AbstractArguments(_AbstractArgumentsMixin): 

137 context = None 

138 argument_node: Any = None 

139 trailer = None 

140 

141 

142def unpack_arglist(arglist): 

143 if arglist is None: 

144 return 

145 

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

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

148 yield 0, arglist 

149 return 

150 

151 iterator = iter(arglist.children) 

152 for child in iterator: 

153 if child == ',': 

154 continue 

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

156 c = next(iterator, None) 

157 assert c is not None 

158 yield len(child.value), c 

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

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

161 assert len(child.children) == 2 

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

163 else: 

164 yield 0, child 

165 

166 

167class TreeArguments(AbstractArguments): 

168 context: Any 

169 

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

171 """ 

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

173 """ 

174 self.argument_node = argument_node 

175 self.context = context 

176 self._inference_state = inference_state 

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

178 

179 @classmethod 

180 @inference_state_as_method_param_cache() 

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

182 return cls(*args, **kwargs) 

183 

184 def unpack(self, funcdef=None): 

185 named_args = [] 

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

187 if star_count == 1: 

188 arrays = self.context.infer_node(el) 

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

190 for a in arrays] 

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

192 yield None, get_merged_lazy_value( 

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

194 ) 

195 elif star_count == 2: 

196 arrays = self.context.infer_node(el) 

197 for dct in arrays: 

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

199 else: 

200 if el.type == 'argument': 

201 c = el.children 

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

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

204 else: # Generator comprehension. 

205 # Include the brackets with the parent. 

206 sync_comp_for = el.children[1] 

207 if sync_comp_for.type == 'comp_for': 

208 sync_comp_for = sync_comp_for.children[1] 

209 comp = iterable.GeneratorComprehension( 

210 self._inference_state, 

211 defining_context=self.context, 

212 sync_comp_for_node=sync_comp_for, 

213 entry_node=el.children[0], 

214 ) 

215 yield None, LazyKnownValue(comp) 

216 else: 

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

218 

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

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

221 yield from named_args 

222 

223 def _as_tree_tuple_objects(self): 

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

225 default = None 

226 if argument.type == 'argument': 

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

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

229 yield argument, default, star_count 

230 

231 def iter_calling_names_with_star(self): 

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

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

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

235 continue 

236 

237 yield TreeNameDefinition(self.context, name) 

238 

239 def __repr__(self): 

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

241 

242 def get_calling_nodes(self): 

243 old_arguments_list = [] 

244 arguments = self 

245 

246 while arguments not in old_arguments_list: 

247 if not isinstance(arguments, TreeArguments): 

248 break 

249 

250 old_arguments_list.append(arguments) 

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

252 names = calling_name.goto() 

253 if len(names) != 1: 

254 break 

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

256 # Dynamic parameters should not have calling nodes, because 

257 # they are dynamic and extremely random. 

258 return [] 

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

260 break 

261 executed_param_name = names[0].get_executed_param_name() 

262 arguments = executed_param_name.arguments 

263 break 

264 

265 if arguments.argument_node is not None: 

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

267 if arguments.trailer is not None: 

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

269 return [] 

270 

271 

272class ValuesArguments(AbstractArguments): 

273 def __init__(self, values_list): 

274 self._values_list = values_list 

275 

276 def unpack(self, funcdef=None): 

277 for values in self._values_list: 

278 yield None, LazyKnownValues(values) 

279 

280 def __repr__(self): 

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

282 

283 

284class TreeArgumentsWrapper(_AbstractArgumentsMixin): 

285 def __init__(self, arguments): 

286 self._wrapped_arguments = arguments 

287 

288 @property 

289 def context(self): 

290 return self._wrapped_arguments.context 

291 

292 @property 

293 def argument_node(self): 

294 return self._wrapped_arguments.argument_node 

295 

296 @property 

297 def trailer(self): 

298 return self._wrapped_arguments.trailer 

299 

300 def unpack(self, func=None): 

301 raise NotImplementedError 

302 

303 def get_calling_nodes(self): 

304 return self._wrapped_arguments.get_calling_nodes() 

305 

306 def __repr__(self): 

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

308 

309 

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

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

312 if funcdef is not None: 

313 # TODO this funcdef should not be needed. 

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

315 % (funcdef.name.value, array) 

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

317 try: 

318 iter_ = array.py__iter__ 

319 except AttributeError: 

320 pass 

321 else: 

322 yield from iter_() 

323 

324 

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

326 from jedi.inference.value.instance import CompiledInstance 

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

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

329 # make one call without crazy isinstance checks. 

330 return {} 

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

332 return array.exact_key_items() 

333 else: 

334 if funcdef is not None: 

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

336 % (funcdef.name.value, array) 

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

338 return {}