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

293 statements  

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

1from abc import abstractmethod 

2from contextlib import contextmanager 

3from pathlib import Path 

4from typing import Optional 

5 

6from parso.tree import search_ancestor 

7from parso.python.tree import Name 

8 

9from jedi.inference.filters import ParserTreeFilter, MergedFilter, \ 

10 GlobalNameFilter 

11from jedi.inference.names import AnonymousParamName, TreeNameDefinition 

12from jedi.inference.base_value import NO_VALUES, ValueSet 

13from jedi.parser_utils import get_parent_scope 

14from jedi import debug 

15from jedi import parser_utils 

16 

17 

18class AbstractContext: 

19 # Must be defined: inference_state and tree_node and parent_context as an attribute/property 

20 

21 def __init__(self, inference_state): 

22 self.inference_state = inference_state 

23 self.predefined_names = {} 

24 

25 @abstractmethod 

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

27 raise NotImplementedError 

28 

29 def goto(self, name_or_str, position): 

30 from jedi.inference import finder 

31 filters = _get_global_filters_for_name( 

32 self, name_or_str if isinstance(name_or_str, Name) else None, position, 

33 ) 

34 names = finder.filter_name(filters, name_or_str) 

35 debug.dbg('context.goto %s in (%s): %s', name_or_str, self, names) 

36 return names 

37 

38 def py__getattribute__(self, name_or_str, name_context=None, position=None, 

39 analysis_errors=True): 

40 """ 

41 :param position: Position of the last statement -> tuple of line, column 

42 """ 

43 if name_context is None: 

44 name_context = self 

45 names = self.goto(name_or_str, position) 

46 

47 string_name = name_or_str.value if isinstance(name_or_str, Name) else name_or_str 

48 

49 # This paragraph is currently needed for proper branch type inference 

50 # (static analysis). 

51 found_predefined_types = None 

52 if self.predefined_names and isinstance(name_or_str, Name): 

53 node = name_or_str 

54 while node is not None and not parser_utils.is_scope(node): 

55 node = node.parent 

56 if node.type in ("if_stmt", "for_stmt", "comp_for", 'sync_comp_for'): 

57 try: 

58 name_dict = self.predefined_names[node] 

59 types = name_dict[string_name] 

60 except KeyError: 

61 continue 

62 else: 

63 found_predefined_types = types 

64 break 

65 if found_predefined_types is not None and names: 

66 from jedi.inference import flow_analysis 

67 check = flow_analysis.reachability_check( 

68 context=self, 

69 value_scope=self.tree_node, 

70 node=name_or_str, 

71 ) 

72 if check is flow_analysis.UNREACHABLE: 

73 values = NO_VALUES 

74 else: 

75 values = found_predefined_types 

76 else: 

77 values = ValueSet.from_sets(name.infer() for name in names) 

78 

79 if not names and not values and analysis_errors: 

80 if isinstance(name_or_str, Name): 

81 from jedi.inference import analysis 

82 message = ("NameError: name '%s' is not defined." % string_name) 

83 analysis.add(name_context, 'name-error', name_or_str, message) 

84 

85 debug.dbg('context.names_to_types: %s -> %s', names, values) 

86 if values: 

87 return values 

88 return self._check_for_additional_knowledge(name_or_str, name_context, position) 

89 

90 def _check_for_additional_knowledge(self, name_or_str, name_context, position): 

91 name_context = name_context or self 

92 # Add isinstance and other if/assert knowledge. 

93 if isinstance(name_or_str, Name) and not name_context.is_instance(): 

94 flow_scope = name_or_str 

95 base_nodes = [name_context.tree_node] 

96 

97 if any(b.type in ('comp_for', 'sync_comp_for') for b in base_nodes): 

98 return NO_VALUES 

99 from jedi.inference.finder import check_flow_information 

100 while True: 

101 flow_scope = get_parent_scope(flow_scope, include_flows=True) 

102 n = check_flow_information(name_context, flow_scope, 

103 name_or_str, position) 

104 if n is not None: 

105 return n 

106 if flow_scope in base_nodes: 

107 break 

108 return NO_VALUES 

109 

110 def get_root_context(self): 

111 parent_context = self.parent_context 

112 if parent_context is None: 

113 return self 

114 return parent_context.get_root_context() 

115 

116 def is_module(self): 

117 return False 

118 

119 def is_builtins_module(self): 

120 return False 

121 

122 def is_class(self): 

123 return False 

124 

125 def is_stub(self): 

126 return False 

127 

128 def is_instance(self): 

129 return False 

130 

131 def is_compiled(self): 

132 return False 

133 

134 def is_bound_method(self): 

135 return False 

136 

137 @abstractmethod 

138 def py__name__(self): 

139 raise NotImplementedError 

140 

141 def get_value(self): 

142 raise NotImplementedError 

143 

144 @property 

145 def name(self): 

146 return None 

147 

148 def get_qualified_names(self): 

149 return () 

150 

151 def py__doc__(self): 

152 return '' 

153 

154 @contextmanager 

155 def predefine_names(self, flow_scope, dct): 

156 predefined = self.predefined_names 

157 predefined[flow_scope] = dct 

158 try: 

159 yield 

160 finally: 

161 del predefined[flow_scope] 

162 

163 

164class ValueContext(AbstractContext): 

165 """ 

166 Should be defined, otherwise the API returns empty types. 

167 """ 

168 def __init__(self, value): 

169 super().__init__(value.inference_state) 

170 self._value = value 

171 

172 @property 

173 def tree_node(self): 

174 return self._value.tree_node 

175 

176 @property 

177 def parent_context(self): 

178 return self._value.parent_context 

179 

180 def is_module(self): 

181 return self._value.is_module() 

182 

183 def is_builtins_module(self): 

184 return self._value == self.inference_state.builtins_module 

185 

186 def is_class(self): 

187 return self._value.is_class() 

188 

189 def is_stub(self): 

190 return self._value.is_stub() 

191 

192 def is_instance(self): 

193 return self._value.is_instance() 

194 

195 def is_compiled(self): 

196 return self._value.is_compiled() 

197 

198 def is_bound_method(self): 

199 return self._value.is_bound_method() 

200 

201 def py__name__(self): 

202 return self._value.py__name__() 

203 

204 @property 

205 def name(self): 

206 return self._value.name 

207 

208 def get_qualified_names(self): 

209 return self._value.get_qualified_names() 

210 

211 def py__doc__(self): 

212 return self._value.py__doc__() 

213 

214 def get_value(self): 

215 return self._value 

216 

217 def __repr__(self): 

218 return '%s(%s)' % (self.__class__.__name__, self._value) 

219 

220 

221class TreeContextMixin: 

222 def infer_node(self, node): 

223 from jedi.inference.syntax_tree import infer_node 

224 return infer_node(self, node) 

225 

226 def create_value(self, node): 

227 from jedi.inference import value 

228 

229 if node == self.tree_node: 

230 assert self.is_module() 

231 return self.get_value() 

232 

233 parent_context = self.create_context(node) 

234 

235 if node.type in ('funcdef', 'lambdef'): 

236 func = value.FunctionValue.from_context(parent_context, node) 

237 if parent_context.is_class(): 

238 class_value = parent_context.parent_context.create_value(parent_context.tree_node) 

239 instance = value.AnonymousInstance( 

240 self.inference_state, parent_context.parent_context, class_value) 

241 func = value.BoundMethod( 

242 instance=instance, 

243 class_context=class_value.as_context(), 

244 function=func 

245 ) 

246 return func 

247 elif node.type == 'classdef': 

248 return value.ClassValue(self.inference_state, parent_context, node) 

249 else: 

250 raise NotImplementedError("Probably shouldn't happen: %s" % node) 

251 

252 def create_context(self, node): 

253 def from_scope_node(scope_node, is_nested=True): 

254 if scope_node == self.tree_node: 

255 return self 

256 

257 if scope_node.type in ('funcdef', 'lambdef', 'classdef'): 

258 return self.create_value(scope_node).as_context() 

259 elif scope_node.type in ('comp_for', 'sync_comp_for'): 

260 parent_context = from_scope_node(parent_scope(scope_node.parent)) 

261 if node.start_pos >= scope_node.children[-1].start_pos: 

262 return parent_context 

263 return CompForContext(parent_context, scope_node) 

264 raise Exception("There's a scope that was not managed: %s" % scope_node) 

265 

266 def parent_scope(node): 

267 while True: 

268 node = node.parent 

269 

270 if parser_utils.is_scope(node): 

271 return node 

272 elif node.type in ('argument', 'testlist_comp'): 

273 if node.children[1].type in ('comp_for', 'sync_comp_for'): 

274 return node.children[1] 

275 elif node.type == 'dictorsetmaker': 

276 for n in node.children[1:4]: 

277 # In dictionaries it can be pretty much anything. 

278 if n.type in ('comp_for', 'sync_comp_for'): 

279 return n 

280 

281 scope_node = parent_scope(node) 

282 if scope_node.type in ('funcdef', 'classdef'): 

283 colon = scope_node.children[scope_node.children.index(':')] 

284 if node.start_pos < colon.start_pos: 

285 parent = node.parent 

286 if not (parent.type == 'param' and parent.name == node): 

287 scope_node = parent_scope(scope_node) 

288 return from_scope_node(scope_node, is_nested=True) 

289 

290 def create_name(self, tree_name): 

291 definition = tree_name.get_definition() 

292 if definition and definition.type == 'param' and definition.name == tree_name: 

293 funcdef = search_ancestor(definition, 'funcdef', 'lambdef') 

294 func = self.create_value(funcdef) 

295 return AnonymousParamName(func, tree_name) 

296 else: 

297 context = self.create_context(tree_name) 

298 return TreeNameDefinition(context, tree_name) 

299 

300 

301class FunctionContext(TreeContextMixin, ValueContext): 

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

303 yield ParserTreeFilter( 

304 self.inference_state, 

305 parent_context=self, 

306 until_position=until_position, 

307 origin_scope=origin_scope 

308 ) 

309 

310 

311class ModuleContext(TreeContextMixin, ValueContext): 

312 def py__file__(self) -> Optional[Path]: 

313 return self._value.py__file__() # type: ignore[no-any-return] 

314 

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

316 filters = self._value.get_filters(origin_scope) 

317 # Skip the first filter and replace it. 

318 next(filters, None) 

319 yield MergedFilter( 

320 ParserTreeFilter( 

321 parent_context=self, 

322 until_position=until_position, 

323 origin_scope=origin_scope 

324 ), 

325 self.get_global_filter(), 

326 ) 

327 yield from filters 

328 

329 def get_global_filter(self): 

330 return GlobalNameFilter(self) 

331 

332 @property 

333 def string_names(self): 

334 return self._value.string_names 

335 

336 @property 

337 def code_lines(self): 

338 return self._value.code_lines 

339 

340 def get_value(self): 

341 """ 

342 This is the only function that converts a context back to a value. 

343 This is necessary for stub -> python conversion and vice versa. However 

344 this method shouldn't be moved to AbstractContext. 

345 """ 

346 return self._value 

347 

348 

349class NamespaceContext(TreeContextMixin, ValueContext): 

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

351 return self._value.get_filters() 

352 

353 def get_value(self): 

354 return self._value 

355 

356 @property 

357 def string_names(self): 

358 return self._value.string_names 

359 

360 def py__file__(self) -> Optional[Path]: 

361 return self._value.py__file__() # type: ignore[no-any-return] 

362 

363 

364class ClassContext(TreeContextMixin, ValueContext): 

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

366 yield self.get_global_filter(until_position, origin_scope) 

367 

368 def get_global_filter(self, until_position=None, origin_scope=None): 

369 return ParserTreeFilter( 

370 parent_context=self, 

371 until_position=until_position, 

372 origin_scope=origin_scope 

373 ) 

374 

375 

376class CompForContext(TreeContextMixin, AbstractContext): 

377 def __init__(self, parent_context, comp_for): 

378 super().__init__(parent_context.inference_state) 

379 self.tree_node = comp_for 

380 self.parent_context = parent_context 

381 

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

383 yield ParserTreeFilter(self) 

384 

385 def get_value(self): 

386 return None 

387 

388 def py__name__(self): 

389 return '<comprehension context>' 

390 

391 def __repr__(self): 

392 return '%s(%s)' % (self.__class__.__name__, self.tree_node) 

393 

394 

395class CompiledContext(ValueContext): 

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

397 return self._value.get_filters() 

398 

399 

400class CompiledModuleContext(CompiledContext): 

401 code_lines = None 

402 

403 def get_value(self): 

404 return self._value 

405 

406 @property 

407 def string_names(self): 

408 return self._value.string_names 

409 

410 def py__file__(self) -> Optional[Path]: 

411 return self._value.py__file__() # type: ignore[no-any-return] 

412 

413 

414def _get_global_filters_for_name(context, name_or_none, position): 

415 # For functions and classes the defaults don't belong to the 

416 # function and get inferred in the value before the function. So 

417 # make sure to exclude the function/class name. 

418 if name_or_none is not None: 

419 ancestor = search_ancestor(name_or_none, 'funcdef', 'classdef', 'lambdef') 

420 lambdef = None 

421 if ancestor == 'lambdef': 

422 # For lambdas it's even more complicated since parts will 

423 # be inferred later. 

424 lambdef = ancestor 

425 ancestor = search_ancestor(name_or_none, 'funcdef', 'classdef') 

426 if ancestor is not None: 

427 colon = ancestor.children[-2] 

428 if position is not None and position < colon.start_pos: 

429 if lambdef is None or position < lambdef.children[-2].start_pos: 

430 position = ancestor.start_pos 

431 

432 return get_global_filters(context, position, name_or_none) 

433 

434 

435def get_global_filters(context, until_position, origin_scope): 

436 """ 

437 Returns all filters in order of priority for name resolution. 

438 

439 For global name lookups. The filters will handle name resolution 

440 themselves, but here we gather possible filters downwards. 

441 

442 >>> from jedi import Script 

443 >>> script = Script(''' 

444 ... x = ['a', 'b', 'c'] 

445 ... def func(): 

446 ... y = None 

447 ... ''') 

448 >>> module_node = script._module_node 

449 >>> scope = next(module_node.iter_funcdefs()) 

450 >>> scope 

451 <Function: func@3-5> 

452 >>> context = script._get_module_context().create_context(scope) 

453 >>> filters = list(get_global_filters(context, (4, 0), None)) 

454 

455 First we get the names from the function scope. 

456 

457 >>> print(filters[0]) # doctest: +ELLIPSIS 

458 MergedFilter(<ParserTreeFilter: ...>, <GlobalNameFilter: ...>) 

459 >>> sorted(str(n) for n in filters[0].values()) # doctest: +NORMALIZE_WHITESPACE 

460 ['<TreeNameDefinition: string_name=func start_pos=(3, 4)>', 

461 '<TreeNameDefinition: string_name=x start_pos=(2, 0)>'] 

462 >>> filters[0]._filters[0]._until_position 

463 (4, 0) 

464 >>> filters[0]._filters[1]._until_position 

465 

466 Then it yields the names from one level "lower". In this example, this is 

467 the module scope (including globals). 

468 As a side note, you can see, that the position in the filter is None on the 

469 globals filter, because there the whole module is searched. 

470 

471 >>> list(filters[1].values()) # package modules -> Also empty. 

472 [] 

473 >>> sorted(name.string_name for name in filters[2].values()) # Module attributes 

474 ['__doc__', '__name__', '__package__'] 

475 

476 Finally, it yields the builtin filter, if `include_builtin` is 

477 true (default). 

478 

479 >>> list(filters[3].values()) # doctest: +ELLIPSIS 

480 [...] 

481 """ 

482 base_context = context 

483 from jedi.inference.value.function import BaseFunctionExecutionContext 

484 while context is not None: 

485 # Names in methods cannot be resolved within the class. 

486 yield from context.get_filters( 

487 until_position=until_position, 

488 origin_scope=origin_scope 

489 ) 

490 if isinstance(context, (BaseFunctionExecutionContext, ModuleContext)): 

491 # The position should be reset if the current scope is a function. 

492 until_position = None 

493 

494 context = context.parent_context 

495 

496 b = next(base_context.inference_state.builtins_module.get_filters(), None) 

497 assert b is not None 

498 # Add builtins to the global scope. 

499 yield b