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
« 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
6from parso.tree import search_ancestor
7from parso.python.tree import Name
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
18class AbstractContext:
19 # Must be defined: inference_state and tree_node and parent_context as an attribute/property
21 def __init__(self, inference_state):
22 self.inference_state = inference_state
23 self.predefined_names = {}
25 @abstractmethod
26 def get_filters(self, until_position=None, origin_scope=None):
27 raise NotImplementedError
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
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)
47 string_name = name_or_str.value if isinstance(name_or_str, Name) else name_or_str
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)
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)
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)
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]
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
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()
116 def is_module(self):
117 return False
119 def is_builtins_module(self):
120 return False
122 def is_class(self):
123 return False
125 def is_stub(self):
126 return False
128 def is_instance(self):
129 return False
131 def is_compiled(self):
132 return False
134 def is_bound_method(self):
135 return False
137 @abstractmethod
138 def py__name__(self):
139 raise NotImplementedError
141 def get_value(self):
142 raise NotImplementedError
144 @property
145 def name(self):
146 return None
148 def get_qualified_names(self):
149 return ()
151 def py__doc__(self):
152 return ''
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]
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
172 @property
173 def tree_node(self):
174 return self._value.tree_node
176 @property
177 def parent_context(self):
178 return self._value.parent_context
180 def is_module(self):
181 return self._value.is_module()
183 def is_builtins_module(self):
184 return self._value == self.inference_state.builtins_module
186 def is_class(self):
187 return self._value.is_class()
189 def is_stub(self):
190 return self._value.is_stub()
192 def is_instance(self):
193 return self._value.is_instance()
195 def is_compiled(self):
196 return self._value.is_compiled()
198 def is_bound_method(self):
199 return self._value.is_bound_method()
201 def py__name__(self):
202 return self._value.py__name__()
204 @property
205 def name(self):
206 return self._value.name
208 def get_qualified_names(self):
209 return self._value.get_qualified_names()
211 def py__doc__(self):
212 return self._value.py__doc__()
214 def get_value(self):
215 return self._value
217 def __repr__(self):
218 return '%s(%s)' % (self.__class__.__name__, self._value)
221class TreeContextMixin:
222 def infer_node(self, node):
223 from jedi.inference.syntax_tree import infer_node
224 return infer_node(self, node)
226 def create_value(self, node):
227 from jedi.inference import value
229 if node == self.tree_node:
230 assert self.is_module()
231 return self.get_value()
233 parent_context = self.create_context(node)
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)
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
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)
266 def parent_scope(node):
267 while True:
268 node = node.parent
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
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)
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)
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 )
311class ModuleContext(TreeContextMixin, ValueContext):
312 def py__file__(self) -> Optional[Path]:
313 return self._value.py__file__() # type: ignore[no-any-return]
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
329 def get_global_filter(self):
330 return GlobalNameFilter(self)
332 @property
333 def string_names(self):
334 return self._value.string_names
336 @property
337 def code_lines(self):
338 return self._value.code_lines
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
349class NamespaceContext(TreeContextMixin, ValueContext):
350 def get_filters(self, until_position=None, origin_scope=None):
351 return self._value.get_filters()
353 def get_value(self):
354 return self._value
356 @property
357 def string_names(self):
358 return self._value.string_names
360 def py__file__(self) -> Optional[Path]:
361 return self._value.py__file__() # type: ignore[no-any-return]
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)
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 )
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
382 def get_filters(self, until_position=None, origin_scope=None):
383 yield ParserTreeFilter(self)
385 def get_value(self):
386 return None
388 def py__name__(self):
389 return '<comprehension context>'
391 def __repr__(self):
392 return '%s(%s)' % (self.__class__.__name__, self.tree_node)
395class CompiledContext(ValueContext):
396 def get_filters(self, until_position=None, origin_scope=None):
397 return self._value.get_filters()
400class CompiledModuleContext(CompiledContext):
401 code_lines = None
403 def get_value(self):
404 return self._value
406 @property
407 def string_names(self):
408 return self._value.string_names
410 def py__file__(self) -> Optional[Path]:
411 return self._value.py__file__() # type: ignore[no-any-return]
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
432 return get_global_filters(context, position, name_or_none)
435def get_global_filters(context, until_position, origin_scope):
436 """
437 Returns all filters in order of priority for name resolution.
439 For global name lookups. The filters will handle name resolution
440 themselves, but here we gather possible filters downwards.
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))
455 First we get the names from the function scope.
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
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.
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__']
476 Finally, it yields the builtin filter, if `include_builtin` is
477 true (default).
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
494 context = context.parent_context
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