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

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

302 statements  

1from abc import abstractmethod 

2from contextlib import contextmanager 

3from pathlib import Path 

4from typing import Optional, Any 

5 

6from parso.python.tree import Name 

7 

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

9 GlobalNameFilter 

10from jedi.inference.names import AnonymousParamName, TreeNameDefinition 

11from jedi.inference.base_value import NO_VALUES, ValueSet 

12from jedi.parser_utils import get_parent_scope 

13from jedi import debug 

14from jedi import parser_utils 

15 

16 

17class AbstractContext: 

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

19 tree_node: Any 

20 parent_context: Any 

21 

22 def __init__(self, inference_state): 

23 self.inference_state = inference_state 

24 self.predefined_names = {} 

25 

26 @abstractmethod 

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

28 raise NotImplementedError 

29 

30 def goto(self, name_or_str, position): 

31 from jedi.inference import finder 

32 filters = _get_global_filters_for_name( 

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

34 ) 

35 names = finder.filter_name(filters, name_or_str) 

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

37 return names 

38 

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

40 analysis_errors=True): 

41 """ 

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

43 """ 

44 if name_context is None: 

45 name_context = self 

46 names = self.goto(name_or_str, position) 

47 

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

49 

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

51 # (static analysis). 

52 found_predefined_types = None 

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

54 node = name_or_str 

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

56 node = node.parent 

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

58 try: 

59 name_dict = self.predefined_names[node] 

60 types = name_dict[string_name] 

61 except KeyError: 

62 continue 

63 else: 

64 found_predefined_types = types 

65 break 

66 if found_predefined_types is not None and names: 

67 from jedi.inference import flow_analysis 

68 check = flow_analysis.reachability_check( 

69 context=self, 

70 value_scope=self.tree_node, 

71 node=name_or_str, 

72 ) 

73 if check is flow_analysis.UNREACHABLE: 

74 values = NO_VALUES 

75 else: 

76 values = found_predefined_types 

77 else: 

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

79 

80 if not names and not values and analysis_errors: 

81 if isinstance(name_or_str, Name): 

82 from jedi.inference import analysis 

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

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

85 

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

87 if values: 

88 return values 

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

90 

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

92 name_context = name_context or self 

93 # Add isinstance and other if/assert knowledge. 

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

95 flow_scope = name_or_str 

96 base_nodes = [name_context.tree_node] 

97 

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

99 return NO_VALUES 

100 from jedi.inference.finder import check_flow_information 

101 while True: 

102 flow_scope = get_parent_scope(flow_scope, include_flows=True) 

103 n = check_flow_information(name_context, flow_scope, 

104 name_or_str, position) 

105 if n is not None: 

106 return n 

107 if flow_scope in base_nodes: 

108 break 

109 return NO_VALUES 

110 

111 def get_root_context(self): 

112 parent_context = self.parent_context 

113 if parent_context is None: 

114 return self 

115 return parent_context.get_root_context() 

116 

117 def is_module(self): 

118 return False 

119 

120 def is_builtins_module(self): 

121 return False 

122 

123 def is_class(self): 

124 return False 

125 

126 def is_stub(self): 

127 return False 

128 

129 def is_instance(self): 

130 return False 

131 

132 def is_compiled(self): 

133 return False 

134 

135 def is_bound_method(self): 

136 return False 

137 

138 @abstractmethod 

139 def py__name__(self): 

140 raise NotImplementedError 

141 

142 def get_value(self): 

143 raise NotImplementedError 

144 

145 @property 

146 def name(self): 

147 return None 

148 

149 def get_qualified_names(self): 

150 return () 

151 

152 def py__doc__(self): 

153 return '' 

154 

155 @contextmanager 

156 def predefine_names(self, flow_scope, dct): 

157 predefined = self.predefined_names 

158 predefined[flow_scope] = dct 

159 try: 

160 yield 

161 finally: 

162 del predefined[flow_scope] 

163 

164 

165class ValueContext(AbstractContext): 

166 """ 

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

168 """ 

169 def __init__(self, value): 

170 super().__init__(value.inference_state) 

171 self._value = value 

172 

173 @property 

174 def tree_node(self): 

175 return self._value.tree_node 

176 

177 @property 

178 def parent_context(self): 

179 return self._value.parent_context 

180 

181 def is_module(self): 

182 return self._value.is_module() 

183 

184 def is_builtins_module(self): 

185 return self._value == self.inference_state.builtins_module 

186 

187 def is_class(self): 

188 return self._value.is_class() 

189 

190 def is_stub(self): 

191 return self._value.is_stub() 

192 

193 def is_instance(self): 

194 return self._value.is_instance() 

195 

196 def is_compiled(self): 

197 return self._value.is_compiled() 

198 

199 def is_bound_method(self): 

200 return self._value.is_bound_method() 

201 

202 def py__name__(self): 

203 return self._value.py__name__() 

204 

205 @property 

206 def name(self): 

207 return self._value.name 

208 

209 def get_qualified_names(self): 

210 return self._value.get_qualified_names() 

211 

212 def py__doc__(self): 

213 return self._value.py__doc__() 

214 

215 def get_value(self): 

216 return self._value 

217 

218 def __repr__(self): 

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

220 

221 

222class TreeContextMixin: 

223 tree_node: Any 

224 is_module: Any 

225 get_value: Any 

226 inference_state: Any 

227 is_class: Any 

228 parent_context: Any 

229 

230 def infer_node(self, node): 

231 from jedi.inference.syntax_tree import infer_node 

232 return infer_node(self, node) 

233 

234 def create_value(self, node): 

235 from jedi.inference import value 

236 

237 if node == self.tree_node: 

238 assert self.is_module() 

239 return self.get_value() 

240 

241 parent_context = self.create_context(node) 

242 

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

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

245 if parent_context.is_class(): 

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

247 instance = value.AnonymousInstance( 

248 self.inference_state, parent_context.parent_context, class_value) 

249 func = value.BoundMethod( 

250 instance=instance, 

251 class_context=class_value.as_context(), 

252 function=func 

253 ) 

254 return func 

255 elif node.type == 'classdef': 

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

257 else: 

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

259 

260 def create_context(self, node): 

261 def from_scope_node(scope_node, is_nested=True): 

262 if scope_node == self.tree_node: 

263 return self 

264 

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

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

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

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

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

270 return parent_context 

271 return CompForContext(parent_context, scope_node) 

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

273 

274 def parent_scope(node): 

275 while True: 

276 node = node.parent 

277 

278 if parser_utils.is_scope(node): 

279 return node 

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

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

282 return node.children[1] 

283 elif node.type == 'dictorsetmaker': 

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

285 # In dictionaries it can be pretty much anything. 

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

287 return n 

288 

289 scope_node = parent_scope(node) 

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

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

292 if node.start_pos < colon.start_pos: 

293 parent = node.parent 

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

295 scope_node = parent_scope(scope_node) 

296 return from_scope_node(scope_node, is_nested=True) 

297 

298 def create_name(self, tree_name): 

299 definition = tree_name.get_definition() 

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

301 funcdef = definition.search_ancestor('funcdef', 'lambdef') 

302 func = self.create_value(funcdef) 

303 return AnonymousParamName(func, tree_name) 

304 else: 

305 context = self.create_context(tree_name) 

306 return TreeNameDefinition(context, tree_name) 

307 

308 

309class FunctionContext(TreeContextMixin, ValueContext): 

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

311 yield ParserTreeFilter( 

312 parent_context=self, 

313 until_position=until_position, 

314 origin_scope=origin_scope 

315 ) 

316 

317 

318class ModuleContext(TreeContextMixin, ValueContext): 

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

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

321 

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

323 filters = self._value.get_filters(origin_scope) 

324 # Skip the first filter and replace it. 

325 next(filters, None) 

326 yield MergedFilter( 

327 ParserTreeFilter( 

328 parent_context=self, 

329 until_position=until_position, 

330 origin_scope=origin_scope 

331 ), 

332 self.get_global_filter(), 

333 ) 

334 yield from filters 

335 

336 def get_global_filter(self): 

337 return GlobalNameFilter(self) 

338 

339 @property 

340 def string_names(self): 

341 return self._value.string_names 

342 

343 @property 

344 def code_lines(self): 

345 return self._value.code_lines 

346 

347 def get_value(self): 

348 """ 

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

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

351 this method shouldn't be moved to AbstractContext. 

352 """ 

353 return self._value 

354 

355 

356class NamespaceContext(TreeContextMixin, ValueContext): 

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

358 return self._value.get_filters() 

359 

360 def get_value(self): 

361 return self._value 

362 

363 @property 

364 def string_names(self): 

365 return self._value.string_names 

366 

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

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

369 

370 

371class ClassContext(TreeContextMixin, ValueContext): 

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

373 yield self.get_global_filter(until_position, origin_scope) 

374 

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

376 return ParserTreeFilter( 

377 parent_context=self, 

378 until_position=until_position, 

379 origin_scope=origin_scope 

380 ) 

381 

382 

383class CompForContext(TreeContextMixin, AbstractContext): 

384 def __init__(self, parent_context, comp_for): 

385 super().__init__(parent_context.inference_state) 

386 self.tree_node = comp_for 

387 self.parent_context = parent_context 

388 

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

390 yield ParserTreeFilter(self) 

391 

392 def get_value(self): 

393 return None 

394 

395 def py__name__(self): 

396 return '<comprehension context>' 

397 

398 def __repr__(self): 

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

400 

401 

402class CompiledContext(ValueContext): 

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

404 return self._value.get_filters() 

405 

406 

407class CompiledModuleContext(CompiledContext): 

408 code_lines = None 

409 

410 def get_value(self): 

411 return self._value 

412 

413 @property 

414 def string_names(self): 

415 return self._value.string_names 

416 

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

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

419 

420 

421def _get_global_filters_for_name(context, name_or_none, position): 

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

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

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

425 if name_or_none is not None: 

426 ancestor = name_or_none.search_ancestor('funcdef', 'classdef', 'lambdef') 

427 lambdef = None 

428 if ancestor == 'lambdef': 

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

430 # be inferred later. 

431 lambdef = ancestor 

432 ancestor = name_or_none.search_ancestor('funcdef', 'classdef') 

433 if ancestor is not None: 

434 colon = ancestor.children[-2] 

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

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

437 position = ancestor.start_pos 

438 

439 return get_global_filters(context, position, name_or_none) 

440 

441 

442def get_global_filters(context, until_position, origin_scope): 

443 """ 

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

445 

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

447 themselves, but here we gather possible filters downwards. 

448 

449 >>> from jedi import Script 

450 >>> script = Script(''' 

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

452 ... def func(): 

453 ... y = None 

454 ... ''') 

455 >>> module_node = script._module_node 

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

457 >>> scope 

458 <Function: func@3-5> 

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

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

461 

462 First we get the names from the function scope. 

463 

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

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

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

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

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

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

470 (4, 0) 

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

472 

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

474 the module scope (including globals). 

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

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

477 

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

479 [] 

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

481 ['__doc__', '__name__', '__package__'] 

482 

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

484 true (default). 

485 

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

487 [...] 

488 """ 

489 base_context = context 

490 from jedi.inference.value.function import BaseFunctionExecutionContext 

491 while context is not None: 

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

493 yield from context.get_filters( 

494 until_position=until_position, 

495 origin_scope=origin_scope 

496 ) 

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

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

499 until_position = None 

500 

501 context = context.parent_context 

502 

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

504 assert b is not None 

505 # Add builtins to the global scope. 

506 yield b