1 from abc
import abstractmethod
2 from contextlib
import contextmanager
3 from pathlib
import Path
4 from typing
import Optional
6 from parso
.tree
import search_ancestor
7 from parso
.python
.tree
import Name
9 from jedi
.inference
.filters
import ParserTreeFilter
, MergedFilter
, \
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
18 class 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
= {}
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
,
34 names
= finder
.filter_name(filters
, name_or_str
)
35 debug
.dbg('context.goto %s in (%s): %s', name_or_str
, self
, names
)
38 def py__getattribute__(self
, name_or_str
, name_context
=None, position
=None,
39 analysis_errors
=True):
41 :param position: Position of the last statement -> tuple of line, column
43 if name_context
is None:
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
51 found_predefined_types
= None
52 if self
.predefined_names
and isinstance(name_or_str
, Name
):
54 while node
is not None and not parser_utils
.is_scope(node
):
56 if node
.type in ("if_stmt", "for_stmt", "comp_for", 'sync_comp_for'):
58 name_dict
= self
.predefined_names
[node
]
59 types
= name_dict
[string_name
]
63 found_predefined_types
= types
65 if found_predefined_types
is not None and names
:
66 from jedi
.inference
import flow_analysis
67 check
= flow_analysis
.reachability_check(
69 value_scope
=self
.tree_node
,
72 if check
is flow_analysis
.UNREACHABLE
:
75 values
= found_predefined_types
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
)
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
):
99 from jedi
.inference
.finder
import check_flow_information
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
)
106 if flow_scope
in base_nodes
:
110 def get_root_context(self
):
111 parent_context
= self
.parent_context
112 if parent_context
is None:
114 return parent_context
.get_root_context()
119 def is_builtins_module(self
):
128 def is_instance(self
):
131 def is_compiled(self
):
134 def is_bound_method(self
):
138 def py__name__(self
):
139 raise NotImplementedError
142 raise NotImplementedError
148 def get_qualified_names(self
):
155 def predefine_names(self
, flow_scope
, dct
):
156 predefined
= self
.predefined_names
157 predefined
[flow_scope
] = dct
161 del predefined
[flow_scope
]
164 class ValueContext(AbstractContext
):
166 Should be defined, otherwise the API returns empty types.
168 def __init__(self
, value
):
169 super().__init
__(value
.inference_state
)
174 return self
._value
.tree_node
177 def parent_context(self
):
178 return self
._value
.parent_context
181 return self
._value
.is_module()
183 def is_builtins_module(self
):
184 return self
._value
== self
.inference_state
.builtins_module
187 return self
._value
.is_class()
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__()
206 return self
._value
.name
208 def get_qualified_names(self
):
209 return self
._value
.get_qualified_names()
212 return self
._value
.py__doc__()
218 return '%s(%s)' % (self
.__class
__.__name
__, self
._value
)
221 class 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(
243 class_context
=class_value
.as_context(),
247 elif node
.type == 'classdef':
248 return value
.ClassValue(self
.inference_state
, parent_context
, node
)
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
:
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
):
270 if parser_utils
.is_scope(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'):
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
:
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
)
297 context
= self
.create_context(tree_name
)
298 return TreeNameDefinition(context
, tree_name
)
301 class FunctionContext(TreeContextMixin
, ValueContext
):
302 def get_filters(self
, until_position
=None, origin_scope
=None):
303 yield ParserTreeFilter(
304 self
.inference_state
,
306 until_position
=until_position
,
307 origin_scope
=origin_scope
311 class 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.
322 until_position
=until_position
,
323 origin_scope
=origin_scope
325 self
.get_global_filter(),
329 def get_global_filter(self
):
330 return GlobalNameFilter(self
)
333 def string_names(self
):
334 return self
._value
.string_names
337 def code_lines(self
):
338 return self
._value
.code_lines
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.
349 class NamespaceContext(TreeContextMixin
, ValueContext
):
350 def get_filters(self
, until_position
=None, origin_scope
=None):
351 return self
._value
.get_filters()
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]
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
)
368 def get_global_filter(self
, until_position
=None, origin_scope
=None):
369 return ParserTreeFilter(
371 until_position
=until_position
,
372 origin_scope
=origin_scope
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
382 def get_filters(self
, until_position
=None, origin_scope
=None):
383 yield ParserTreeFilter(self
)
388 def py__name__(self
):
389 return '<comprehension context>'
392 return '%s(%s)' % (self
.__class
__.__name
__, self
.tree_node
)
395 class CompiledContext(ValueContext
):
396 def get_filters(self
, until_position
=None, origin_scope
=None):
397 return self
._value
.get_filters()
400 class CompiledModuleContext(CompiledContext
):
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]
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')
421 if ancestor
== 'lambdef':
422 # For lambdas it's even more complicated since parts will
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
)
435 def get_global_filters(context
, until_position
, origin_scope
):
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']
448 >>> module_node = script._module_node
449 >>> scope = next(module_node.iter_funcdefs())
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
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.
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
479 >>> list(filters[3].values()) # doctest: +ELLIPSIS
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
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)
498 # Add builtins to the global scope.