Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/value/function.py: 27%

296 statements  

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

1from parso.python import tree 

2 

3from jedi import debug 

4from jedi.inference.cache import inference_state_method_cache, CachedMetaClass 

5from jedi.inference import compiled 

6from jedi.inference import recursion 

7from jedi.inference import docstrings 

8from jedi.inference import flow_analysis 

9from jedi.inference.signature import TreeSignature 

10from jedi.inference.filters import ParserTreeFilter, FunctionExecutionFilter, \ 

11 AnonymousFunctionExecutionFilter 

12from jedi.inference.names import ValueName, AbstractNameDefinition, \ 

13 AnonymousParamName, ParamName, NameWrapper 

14from jedi.inference.base_value import ContextualizedNode, NO_VALUES, \ 

15 ValueSet, TreeValue, ValueWrapper 

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

17 LazyTreeValue 

18from jedi.inference.context import ValueContext, TreeContextMixin 

19from jedi.inference.value import iterable 

20from jedi import parser_utils 

21from jedi.inference.parser_cache import get_yield_exprs 

22from jedi.inference.helpers import values_from_qualified_names 

23from jedi.inference.gradual.generics import TupleGenericManager 

24 

25 

26class LambdaName(AbstractNameDefinition): 

27 string_name = '<lambda>' 

28 api_type = 'function' 

29 

30 def __init__(self, lambda_value): 

31 self._lambda_value = lambda_value 

32 self.parent_context = lambda_value.parent_context 

33 

34 @property 

35 def start_pos(self): 

36 return self._lambda_value.tree_node.start_pos 

37 

38 def infer(self): 

39 return ValueSet([self._lambda_value]) 

40 

41 

42class FunctionAndClassBase(TreeValue): 

43 def get_qualified_names(self): 

44 if self.parent_context.is_class(): 

45 n = self.parent_context.get_qualified_names() 

46 if n is None: 

47 # This means that the parent class lives within a function. 

48 return None 

49 return n + (self.py__name__(),) 

50 elif self.parent_context.is_module(): 

51 return (self.py__name__(),) 

52 else: 

53 return None 

54 

55 

56class FunctionMixin: 

57 api_type = 'function' 

58 

59 def get_filters(self, origin_scope=None): 

60 cls = self.py__class__() 

61 for instance in cls.execute_with_values(): 

62 yield from instance.get_filters(origin_scope=origin_scope) 

63 

64 def py__get__(self, instance, class_value): 

65 from jedi.inference.value.instance import BoundMethod 

66 if instance is None: 

67 # Calling the Foo.bar results in the original bar function. 

68 return ValueSet([self]) 

69 return ValueSet([BoundMethod(instance, class_value.as_context(), self)]) 

70 

71 def get_param_names(self): 

72 return [AnonymousParamName(self, param.name) 

73 for param in self.tree_node.get_params()] 

74 

75 @property 

76 def name(self): 

77 if self.tree_node.type == 'lambdef': 

78 return LambdaName(self) 

79 return ValueName(self, self.tree_node.name) 

80 

81 def is_function(self): 

82 return True 

83 

84 def py__name__(self): 

85 return self.name.string_name 

86 

87 def get_type_hint(self, add_class_info=True): 

88 return_annotation = self.tree_node.annotation 

89 if return_annotation is None: 

90 def param_name_to_str(n): 

91 s = n.string_name 

92 annotation = n.infer().get_type_hint() 

93 if annotation is not None: 

94 s += ': ' + annotation 

95 if n.default_node is not None: 

96 s += '=' + n.default_node.get_code(include_prefix=False) 

97 return s 

98 

99 function_execution = self.as_context() 

100 result = function_execution.infer() 

101 return_hint = result.get_type_hint() 

102 body = self.py__name__() + '(%s)' % ', '.join([ 

103 param_name_to_str(n) 

104 for n in function_execution.get_param_names() 

105 ]) 

106 if return_hint is None: 

107 return body 

108 else: 

109 return_hint = return_annotation.get_code(include_prefix=False) 

110 body = self.py__name__() + self.tree_node.children[2].get_code(include_prefix=False) 

111 

112 return body + ' -> ' + return_hint 

113 

114 def py__call__(self, arguments): 

115 function_execution = self.as_context(arguments) 

116 return function_execution.infer() 

117 

118 def _as_context(self, arguments=None): 

119 if arguments is None: 

120 return AnonymousFunctionExecution(self) 

121 return FunctionExecutionContext(self, arguments) 

122 

123 def get_signatures(self): 

124 return [TreeSignature(f) for f in self.get_signature_functions()] 

125 

126 

127class FunctionValue(FunctionMixin, FunctionAndClassBase, metaclass=CachedMetaClass): 

128 @classmethod 

129 def from_context(cls, context, tree_node): 

130 def create(tree_node): 

131 if context.is_class(): 

132 return MethodValue( 

133 context.inference_state, 

134 context, 

135 parent_context=parent_context, 

136 tree_node=tree_node 

137 ) 

138 else: 

139 return cls( 

140 context.inference_state, 

141 parent_context=parent_context, 

142 tree_node=tree_node 

143 ) 

144 

145 overloaded_funcs = list(_find_overload_functions(context, tree_node)) 

146 

147 parent_context = context 

148 while parent_context.is_class() or parent_context.is_instance(): 

149 parent_context = parent_context.parent_context 

150 

151 function = create(tree_node) 

152 

153 if overloaded_funcs: 

154 return OverloadedFunctionValue( 

155 function, 

156 # Get them into the correct order: lower line first. 

157 list(reversed([create(f) for f in overloaded_funcs])) 

158 ) 

159 return function 

160 

161 def py__class__(self): 

162 c, = values_from_qualified_names(self.inference_state, 'types', 'FunctionType') 

163 return c 

164 

165 def get_default_param_context(self): 

166 return self.parent_context 

167 

168 def get_signature_functions(self): 

169 return [self] 

170 

171 

172class FunctionNameInClass(NameWrapper): 

173 def __init__(self, class_context, name): 

174 super().__init__(name) 

175 self._class_context = class_context 

176 

177 def get_defining_qualified_value(self): 

178 return self._class_context.get_value() # Might be None. 

179 

180 

181class MethodValue(FunctionValue): 

182 def __init__(self, inference_state, class_context, *args, **kwargs): 

183 super().__init__(inference_state, *args, **kwargs) 

184 self.class_context = class_context 

185 

186 def get_default_param_context(self): 

187 return self.class_context 

188 

189 def get_qualified_names(self): 

190 # Need to implement this, because the parent value of a method 

191 # value is not the class value but the module. 

192 names = self.class_context.get_qualified_names() 

193 if names is None: 

194 return None 

195 return names + (self.py__name__(),) 

196 

197 @property 

198 def name(self): 

199 return FunctionNameInClass(self.class_context, super().name) 

200 

201 

202class BaseFunctionExecutionContext(ValueContext, TreeContextMixin): 

203 def infer_annotations(self): 

204 raise NotImplementedError 

205 

206 @inference_state_method_cache(default=NO_VALUES) 

207 @recursion.execution_recursion_decorator() 

208 def get_return_values(self, check_yields=False): 

209 funcdef = self.tree_node 

210 if funcdef.type == 'lambdef': 

211 return self.infer_node(funcdef.children[-1]) 

212 

213 if check_yields: 

214 value_set = NO_VALUES 

215 returns = get_yield_exprs(self.inference_state, funcdef) 

216 else: 

217 value_set = self.infer_annotations() 

218 if value_set: 

219 # If there are annotations, prefer them over anything else. 

220 # This will make it faster. 

221 return value_set 

222 value_set |= docstrings.infer_return_types(self._value) 

223 returns = funcdef.iter_return_stmts() 

224 

225 for r in returns: 

226 if check_yields: 

227 value_set |= ValueSet.from_sets( 

228 lazy_value.infer() 

229 for lazy_value in self._get_yield_lazy_value(r) 

230 ) 

231 else: 

232 check = flow_analysis.reachability_check(self, funcdef, r) 

233 if check is flow_analysis.UNREACHABLE: 

234 debug.dbg('Return unreachable: %s', r) 

235 else: 

236 try: 

237 children = r.children 

238 except AttributeError: 

239 ctx = compiled.builtin_from_name(self.inference_state, 'None') 

240 value_set |= ValueSet([ctx]) 

241 else: 

242 value_set |= self.infer_node(children[1]) 

243 if check is flow_analysis.REACHABLE: 

244 debug.dbg('Return reachable: %s', r) 

245 break 

246 return value_set 

247 

248 def _get_yield_lazy_value(self, yield_expr): 

249 if yield_expr.type == 'keyword': 

250 # `yield` just yields None. 

251 ctx = compiled.builtin_from_name(self.inference_state, 'None') 

252 yield LazyKnownValue(ctx) 

253 return 

254 

255 node = yield_expr.children[1] 

256 if node.type == 'yield_arg': # It must be a yield from. 

257 cn = ContextualizedNode(self, node.children[1]) 

258 yield from cn.infer().iterate(cn) 

259 else: 

260 yield LazyTreeValue(self, node) 

261 

262 @recursion.execution_recursion_decorator(default=iter([])) 

263 def get_yield_lazy_values(self, is_async=False): 

264 # TODO: if is_async, wrap yield statements in Awaitable/async_generator_asend 

265 for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef', 

266 'while_stmt', 'if_stmt')) 

267 for y in get_yield_exprs(self.inference_state, self.tree_node)] 

268 

269 # Calculate if the yields are placed within the same for loop. 

270 yields_order = [] 

271 last_for_stmt = None 

272 for yield_, for_stmt in for_parents: 

273 # For really simple for loops we can predict the order. Otherwise 

274 # we just ignore it. 

275 parent = for_stmt.parent 

276 if parent.type == 'suite': 

277 parent = parent.parent 

278 if for_stmt.type == 'for_stmt' and parent == self.tree_node \ 

279 and parser_utils.for_stmt_defines_one_name(for_stmt): # Simplicity for now. 

280 if for_stmt == last_for_stmt: 

281 yields_order[-1][1].append(yield_) 

282 else: 

283 yields_order.append((for_stmt, [yield_])) 

284 elif for_stmt == self.tree_node: 

285 yields_order.append((None, [yield_])) 

286 else: 

287 types = self.get_return_values(check_yields=True) 

288 if types: 

289 yield LazyKnownValues(types, min=0, max=float('inf')) 

290 return 

291 last_for_stmt = for_stmt 

292 

293 for for_stmt, yields in yields_order: 

294 if for_stmt is None: 

295 # No for_stmt, just normal yields. 

296 for yield_ in yields: 

297 yield from self._get_yield_lazy_value(yield_) 

298 else: 

299 input_node = for_stmt.get_testlist() 

300 cn = ContextualizedNode(self, input_node) 

301 ordered = cn.infer().iterate(cn) 

302 ordered = list(ordered) 

303 for lazy_value in ordered: 

304 dct = {str(for_stmt.children[1].value): lazy_value.infer()} 

305 with self.predefine_names(for_stmt, dct): 

306 for yield_in_same_for_stmt in yields: 

307 yield from self._get_yield_lazy_value(yield_in_same_for_stmt) 

308 

309 def merge_yield_values(self, is_async=False): 

310 return ValueSet.from_sets( 

311 lazy_value.infer() 

312 for lazy_value in self.get_yield_lazy_values() 

313 ) 

314 

315 def is_generator(self): 

316 return bool(get_yield_exprs(self.inference_state, self.tree_node)) 

317 

318 def infer(self): 

319 """ 

320 Created to be used by inheritance. 

321 """ 

322 inference_state = self.inference_state 

323 is_coroutine = self.tree_node.parent.type in ('async_stmt', 'async_funcdef') 

324 from jedi.inference.gradual.base import GenericClass 

325 

326 if is_coroutine: 

327 if self.is_generator(): 

328 async_generator_classes = inference_state.typing_module \ 

329 .py__getattribute__('AsyncGenerator') 

330 

331 yield_values = self.merge_yield_values(is_async=True) 

332 # The contravariant doesn't seem to be defined. 

333 generics = (yield_values.py__class__(), NO_VALUES) 

334 return ValueSet( 

335 GenericClass(c, TupleGenericManager(generics)) 

336 for c in async_generator_classes 

337 ).execute_annotation() 

338 else: 

339 async_classes = inference_state.typing_module.py__getattribute__('Coroutine') 

340 return_values = self.get_return_values() 

341 # Only the first generic is relevant. 

342 generics = (return_values.py__class__(), NO_VALUES, NO_VALUES) 

343 return ValueSet( 

344 GenericClass(c, TupleGenericManager(generics)) for c in async_classes 

345 ).execute_annotation() 

346 else: 

347 # If there are annotations, prefer them over anything else. 

348 if self.is_generator() and not self.infer_annotations(): 

349 return ValueSet([iterable.Generator(inference_state, self)]) 

350 else: 

351 return self.get_return_values() 

352 

353 

354class FunctionExecutionContext(BaseFunctionExecutionContext): 

355 def __init__(self, function_value, arguments): 

356 super().__init__(function_value) 

357 self._arguments = arguments 

358 

359 def get_filters(self, until_position=None, origin_scope=None): 

360 yield FunctionExecutionFilter( 

361 self, self._value, 

362 until_position=until_position, 

363 origin_scope=origin_scope, 

364 arguments=self._arguments 

365 ) 

366 

367 def infer_annotations(self): 

368 from jedi.inference.gradual.annotation import infer_return_types 

369 return infer_return_types(self._value, self._arguments) 

370 

371 def get_param_names(self): 

372 return [ 

373 ParamName(self._value, param.name, self._arguments) 

374 for param in self._value.tree_node.get_params() 

375 ] 

376 

377 

378class AnonymousFunctionExecution(BaseFunctionExecutionContext): 

379 def infer_annotations(self): 

380 # I don't think inferring anonymous executions is a big thing. 

381 # Anonymous contexts are mostly there for the user to work in. ~ dave 

382 return NO_VALUES 

383 

384 def get_filters(self, until_position=None, origin_scope=None): 

385 yield AnonymousFunctionExecutionFilter( 

386 self, self._value, 

387 until_position=until_position, 

388 origin_scope=origin_scope, 

389 ) 

390 

391 def get_param_names(self): 

392 return self._value.get_param_names() 

393 

394 

395class OverloadedFunctionValue(FunctionMixin, ValueWrapper): 

396 def __init__(self, function, overloaded_functions): 

397 super().__init__(function) 

398 self._overloaded_functions = overloaded_functions 

399 

400 def py__call__(self, arguments): 

401 debug.dbg("Execute overloaded function %s", self._wrapped_value, color='BLUE') 

402 function_executions = [] 

403 for signature in self.get_signatures(): 

404 function_execution = signature.value.as_context(arguments) 

405 function_executions.append(function_execution) 

406 if signature.matches_signature(arguments): 

407 return function_execution.infer() 

408 

409 if self.inference_state.is_analysis: 

410 # In this case we want precision. 

411 return NO_VALUES 

412 return ValueSet.from_sets(fe.infer() for fe in function_executions) 

413 

414 def get_signature_functions(self): 

415 return self._overloaded_functions 

416 

417 def get_type_hint(self, add_class_info=True): 

418 return 'Union[%s]' % ', '.join(f.get_type_hint() for f in self._overloaded_functions) 

419 

420 

421def _find_overload_functions(context, tree_node): 

422 def _is_overload_decorated(funcdef): 

423 if funcdef.parent.type == 'decorated': 

424 decorators = funcdef.parent.children[0] 

425 if decorators.type == 'decorator': 

426 decorators = [decorators] 

427 else: 

428 decorators = decorators.children 

429 for decorator in decorators: 

430 dotted_name = decorator.children[1] 

431 if dotted_name.type == 'name' and dotted_name.value == 'overload': 

432 # TODO check with values if it's the right overload 

433 return True 

434 return False 

435 

436 if tree_node.type == 'lambdef': 

437 return 

438 

439 if _is_overload_decorated(tree_node): 

440 yield tree_node 

441 

442 while True: 

443 filter = ParserTreeFilter( 

444 context, 

445 until_position=tree_node.start_pos 

446 ) 

447 names = filter.get(tree_node.name.value) 

448 assert isinstance(names, list) 

449 if not names: 

450 break 

451 

452 found = False 

453 for name in names: 

454 funcdef = name.tree_name.parent 

455 if funcdef.type == 'funcdef' and _is_overload_decorated(funcdef): 

456 tree_node = funcdef 

457 found = True 

458 yield funcdef 

459 

460 if not found: 

461 break