]> crepu.dev Git - config.git/blame - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/jedi/inference/context.py
ActualizaciĆ³n de Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / jedi / inference / context.py
CommitLineData
53e6db90
DC
1from abc import abstractmethod
2from contextlib import contextmanager
3from pathlib import Path
4from typing import Optional
5
6from parso.tree import search_ancestor
7from parso.python.tree import Name
8
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
16
17
18class 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
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
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
221class 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
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 )
309
310
311class 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
349class 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
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)
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
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
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
395class CompiledContext(ValueContext):
396 def get_filters(self, until_position=None, origin_scope=None):
397 return self._value.get_filters()
398
399
400class 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
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
431
432 return get_global_filters(context, position, name_or_none)
433
434
435def 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