1 from abc import abstractmethod 2 from contextlib import contextmanager 3 from pathlib import Path 4 from typing import Optional 5 6 from parso.tree import search_ancestor 7 from parso.python.tree import Name 8 9 from jedi.inference.filters import ParserTreeFilter, MergedFilter, \ 10 GlobalNameFilter 11 from jedi.inference.names import AnonymousParamName, TreeNameDefinition 12 from jedi.inference.base_value import NO_VALUES, ValueSet 13 from jedi.parser_utils import get_parent_scope 14 from jedi import debug 15 from jedi import parser_utils 16 17 18 class 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 164 class 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 221 class 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 301 class 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 311 class 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 349 class 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 364 class 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 376 class 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 395 class CompiledContext(ValueContext): 396 def get_filters(self, until_position=None, origin_scope=None): 397 return self._value.get_filters() 398 399 400 class 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 414 def _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 435 def 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 500