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