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

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

294 statements  

1from abc import abstractmethod 

2from contextlib import contextmanager 

3from pathlib import Path 

4from typing import Optional 

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 

20 def __init__(self, inference_state): 

21 self.inference_state = inference_state 

22 self.predefined_names = {} 

23 

24 @abstractmethod 

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

26 raise NotImplementedError 

27 

28 def goto(self, name_or_str, position): 

29 from jedi.inference import finder 

30 filters = _get_global_filters_for_name( 

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

32 ) 

33 names = finder.filter_name(filters, name_or_str) 

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

35 return names 

36 

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

38 analysis_errors=True): 

39 """ 

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

41 """ 

42 if name_context is None: 

43 name_context = self 

44 names = self.goto(name_or_str, position) 

45 

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

47 

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

49 # (static analysis). 

50 found_predefined_types = None 

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

52 node = name_or_str 

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

54 node = node.parent 

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

56 try: 

57 name_dict = self.predefined_names[node] 

58 types = name_dict[string_name] 

59 except KeyError: 

60 continue 

61 else: 

62 found_predefined_types = types 

63 break 

64 if found_predefined_types is not None and names: 

65 from jedi.inference import flow_analysis 

66 check = flow_analysis.reachability_check( 

67 context=self, 

68 value_scope=self.tree_node, 

69 node=name_or_str, 

70 ) 

71 if check is flow_analysis.UNREACHABLE: 

72 values = NO_VALUES 

73 else: 

74 values = found_predefined_types 

75 else: 

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

77 

78 if not names and not values and analysis_errors: 

79 if isinstance(name_or_str, Name): 

80 from jedi.inference import analysis 

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

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

83 

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

85 if values: 

86 return values 

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

88 

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

90 name_context = name_context or self 

91 # Add isinstance and other if/assert knowledge. 

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

93 flow_scope = name_or_str 

94 base_nodes = [name_context.tree_node] 

95 

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

97 return NO_VALUES 

98 from jedi.inference.finder import check_flow_information 

99 while True: 

100 flow_scope = get_parent_scope(flow_scope, include_flows=True) 

101 n = check_flow_information(name_context, flow_scope, 

102 name_or_str, position) 

103 if n is not None: 

104 return n 

105 if flow_scope in base_nodes: 

106 break 

107 return NO_VALUES 

108 

109 def get_root_context(self): 

110 parent_context = self.parent_context 

111 if parent_context is None: 

112 return self 

113 return parent_context.get_root_context() 

114 

115 def is_module(self): 

116 return False 

117 

118 def is_builtins_module(self): 

119 return False 

120 

121 def is_class(self): 

122 return False 

123 

124 def is_stub(self): 

125 return False 

126 

127 def is_instance(self): 

128 return False 

129 

130 def is_compiled(self): 

131 return False 

132 

133 def is_bound_method(self): 

134 return False 

135 

136 @abstractmethod 

137 def py__name__(self): 

138 raise NotImplementedError 

139 

140 def get_value(self): 

141 raise NotImplementedError 

142 

143 @property 

144 def name(self): 

145 return None 

146 

147 def get_qualified_names(self): 

148 return () 

149 

150 def py__doc__(self): 

151 return '' 

152 

153 @contextmanager 

154 def predefine_names(self, flow_scope, dct): 

155 predefined = self.predefined_names 

156 predefined[flow_scope] = dct 

157 try: 

158 yield 

159 finally: 

160 del predefined[flow_scope] 

161 

162 

163class ValueContext(AbstractContext): 

164 """ 

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

166 """ 

167 def __init__(self, value): 

168 super().__init__(value.inference_state) 

169 self._value = value 

170 

171 @property 

172 def tree_node(self): 

173 return self._value.tree_node 

174 

175 @property 

176 def parent_context(self): 

177 return self._value.parent_context 

178 

179 def is_module(self): 

180 return self._value.is_module() 

181 

182 def is_builtins_module(self): 

183 return self._value == self.inference_state.builtins_module 

184 

185 def is_class(self): 

186 return self._value.is_class() 

187 

188 def is_stub(self): 

189 return self._value.is_stub() 

190 

191 def is_instance(self): 

192 return self._value.is_instance() 

193 

194 def is_compiled(self): 

195 return self._value.is_compiled() 

196 

197 def is_bound_method(self): 

198 return self._value.is_bound_method() 

199 

200 def py__name__(self): 

201 return self._value.py__name__() 

202 

203 @property 

204 def name(self): 

205 return self._value.name 

206 

207 def get_qualified_names(self): 

208 return self._value.get_qualified_names() 

209 

210 def py__doc__(self): 

211 return self._value.py__doc__() 

212 

213 def get_value(self): 

214 return self._value 

215 

216 def __repr__(self): 

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

218 

219 

220class TreeContextMixin: 

221 def infer_node(self, node): 

222 from jedi.inference.syntax_tree import infer_node 

223 return infer_node(self, node) 

224 

225 def create_value(self, node): 

226 from jedi.inference import value 

227 

228 if node == self.tree_node: 

229 assert self.is_module() 

230 return self.get_value() 

231 

232 parent_context = self.create_context(node) 

233 

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

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

236 if parent_context.is_class(): 

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

238 instance = value.AnonymousInstance( 

239 self.inference_state, parent_context.parent_context, class_value) 

240 func = value.BoundMethod( 

241 instance=instance, 

242 class_context=class_value.as_context(), 

243 function=func 

244 ) 

245 return func 

246 elif node.type == 'classdef': 

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

248 else: 

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

250 

251 def create_context(self, node): 

252 def from_scope_node(scope_node, is_nested=True): 

253 if scope_node == self.tree_node: 

254 return self 

255 

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

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

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

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

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

261 return parent_context 

262 return CompForContext(parent_context, scope_node) 

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

264 

265 def parent_scope(node): 

266 while True: 

267 node = node.parent 

268 

269 if parser_utils.is_scope(node): 

270 return node 

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

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

273 return node.children[1] 

274 elif node.type == 'dictorsetmaker': 

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

276 # In dictionaries it can be pretty much anything. 

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

278 return n 

279 

280 scope_node = parent_scope(node) 

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

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

283 if node.start_pos < colon.start_pos: 

284 parent = node.parent 

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

286 scope_node = parent_scope(scope_node) 

287 return from_scope_node(scope_node, is_nested=True) 

288 

289 def create_name(self, tree_name): 

290 definition = tree_name.get_definition() 

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

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

293 func = self.create_value(funcdef) 

294 return AnonymousParamName(func, tree_name) 

295 else: 

296 context = self.create_context(tree_name) 

297 return TreeNameDefinition(context, tree_name) 

298 

299 

300class FunctionContext(TreeContextMixin, ValueContext): 

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

302 yield ParserTreeFilter( 

303 self.inference_state, 

304 parent_context=self, 

305 until_position=until_position, 

306 origin_scope=origin_scope 

307 ) 

308 

309 

310class ModuleContext(TreeContextMixin, ValueContext): 

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

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

313 

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

315 filters = self._value.get_filters(origin_scope) 

316 # Skip the first filter and replace it. 

317 next(filters, None) 

318 yield MergedFilter( 

319 ParserTreeFilter( 

320 parent_context=self, 

321 until_position=until_position, 

322 origin_scope=origin_scope 

323 ), 

324 self.get_global_filter(), 

325 ) 

326 yield from filters 

327 

328 def get_global_filter(self): 

329 return GlobalNameFilter(self) 

330 

331 @property 

332 def string_names(self): 

333 return self._value.string_names 

334 

335 @property 

336 def code_lines(self): 

337 return self._value.code_lines 

338 

339 def get_value(self): 

340 """ 

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

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

343 this method shouldn't be moved to AbstractContext. 

344 """ 

345 return self._value 

346 

347 

348class NamespaceContext(TreeContextMixin, ValueContext): 

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

350 return self._value.get_filters() 

351 

352 def get_value(self): 

353 return self._value 

354 

355 @property 

356 def string_names(self): 

357 return self._value.string_names 

358 

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

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

361 

362 

363class ClassContext(TreeContextMixin, ValueContext): 

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

365 yield self.get_global_filter(until_position, origin_scope) 

366 

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

368 return ParserTreeFilter( 

369 parent_context=self, 

370 until_position=until_position, 

371 origin_scope=origin_scope 

372 ) 

373 

374 

375class CompForContext(TreeContextMixin, AbstractContext): 

376 def __init__(self, parent_context, comp_for): 

377 super().__init__(parent_context.inference_state) 

378 self.tree_node = comp_for 

379 self.parent_context = parent_context 

380 

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

382 yield ParserTreeFilter(self) 

383 

384 def get_value(self): 

385 return None 

386 

387 def py__name__(self): 

388 return '<comprehension context>' 

389 

390 def __repr__(self): 

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

392 

393 

394class CompiledContext(ValueContext): 

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

396 return self._value.get_filters() 

397 

398 

399class CompiledModuleContext(CompiledContext): 

400 code_lines = None 

401 

402 def get_value(self): 

403 return self._value 

404 

405 @property 

406 def string_names(self): 

407 return self._value.string_names 

408 

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

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

411 

412 

413def _get_global_filters_for_name(context, name_or_none, position): 

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

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

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

417 if name_or_none is not None: 

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

419 lambdef = None 

420 if ancestor == 'lambdef': 

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

422 # be inferred later. 

423 lambdef = ancestor 

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

425 if ancestor is not None: 

426 colon = ancestor.children[-2] 

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

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

429 position = ancestor.start_pos 

430 

431 return get_global_filters(context, position, name_or_none) 

432 

433 

434def get_global_filters(context, until_position, origin_scope): 

435 """ 

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

437 

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

439 themselves, but here we gather possible filters downwards. 

440 

441 >>> from jedi import Script 

442 >>> script = Script(''' 

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

444 ... def func(): 

445 ... y = None 

446 ... ''') 

447 >>> module_node = script._module_node 

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

449 >>> scope 

450 <Function: func@3-5> 

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

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

453 

454 First we get the names from the function scope. 

455 

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

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

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

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

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

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

462 (4, 0) 

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

464 

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

466 the module scope (including globals). 

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

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

469 

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

471 [] 

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

473 ['__doc__', '__name__', '__package__'] 

474 

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

476 true (default). 

477 

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

479 [...] 

480 """ 

481 base_context = context 

482 from jedi.inference.value.function import BaseFunctionExecutionContext 

483 while context is not None: 

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

485 yield from context.get_filters( 

486 until_position=until_position, 

487 origin_scope=origin_scope 

488 ) 

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

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

491 until_position = None 

492 

493 context = context.parent_context 

494 

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

496 assert b is not None 

497 # Add builtins to the global scope. 

498 yield b