init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
499
.venv/lib/python3.8/site-packages/jedi/inference/context.py
Normal file
499
.venv/lib/python3.8/site-packages/jedi/inference/context.py
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
from abc import abstractmethod
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from parso.tree import search_ancestor
|
||||
from parso.python.tree import Name
|
||||
|
||||
from jedi.inference.filters import ParserTreeFilter, MergedFilter, \
|
||||
GlobalNameFilter
|
||||
from jedi.inference.names import AnonymousParamName, TreeNameDefinition
|
||||
from jedi.inference.base_value import NO_VALUES, ValueSet
|
||||
from jedi.parser_utils import get_parent_scope
|
||||
from jedi import debug
|
||||
from jedi import parser_utils
|
||||
|
||||
|
||||
class AbstractContext:
|
||||
# Must be defined: inference_state and tree_node and parent_context as an attribute/property
|
||||
|
||||
def __init__(self, inference_state):
|
||||
self.inference_state = inference_state
|
||||
self.predefined_names = {}
|
||||
|
||||
@abstractmethod
|
||||
def get_filters(self, until_position=None, origin_scope=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def goto(self, name_or_str, position):
|
||||
from jedi.inference import finder
|
||||
filters = _get_global_filters_for_name(
|
||||
self, name_or_str if isinstance(name_or_str, Name) else None, position,
|
||||
)
|
||||
names = finder.filter_name(filters, name_or_str)
|
||||
debug.dbg('context.goto %s in (%s): %s', name_or_str, self, names)
|
||||
return names
|
||||
|
||||
def py__getattribute__(self, name_or_str, name_context=None, position=None,
|
||||
analysis_errors=True):
|
||||
"""
|
||||
:param position: Position of the last statement -> tuple of line, column
|
||||
"""
|
||||
if name_context is None:
|
||||
name_context = self
|
||||
names = self.goto(name_or_str, position)
|
||||
|
||||
string_name = name_or_str.value if isinstance(name_or_str, Name) else name_or_str
|
||||
|
||||
# This paragraph is currently needed for proper branch type inference
|
||||
# (static analysis).
|
||||
found_predefined_types = None
|
||||
if self.predefined_names and isinstance(name_or_str, Name):
|
||||
node = name_or_str
|
||||
while node is not None and not parser_utils.is_scope(node):
|
||||
node = node.parent
|
||||
if node.type in ("if_stmt", "for_stmt", "comp_for", 'sync_comp_for'):
|
||||
try:
|
||||
name_dict = self.predefined_names[node]
|
||||
types = name_dict[string_name]
|
||||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
found_predefined_types = types
|
||||
break
|
||||
if found_predefined_types is not None and names:
|
||||
from jedi.inference import flow_analysis
|
||||
check = flow_analysis.reachability_check(
|
||||
context=self,
|
||||
value_scope=self.tree_node,
|
||||
node=name_or_str,
|
||||
)
|
||||
if check is flow_analysis.UNREACHABLE:
|
||||
values = NO_VALUES
|
||||
else:
|
||||
values = found_predefined_types
|
||||
else:
|
||||
values = ValueSet.from_sets(name.infer() for name in names)
|
||||
|
||||
if not names and not values and analysis_errors:
|
||||
if isinstance(name_or_str, Name):
|
||||
from jedi.inference import analysis
|
||||
message = ("NameError: name '%s' is not defined." % string_name)
|
||||
analysis.add(name_context, 'name-error', name_or_str, message)
|
||||
|
||||
debug.dbg('context.names_to_types: %s -> %s', names, values)
|
||||
if values:
|
||||
return values
|
||||
return self._check_for_additional_knowledge(name_or_str, name_context, position)
|
||||
|
||||
def _check_for_additional_knowledge(self, name_or_str, name_context, position):
|
||||
name_context = name_context or self
|
||||
# Add isinstance and other if/assert knowledge.
|
||||
if isinstance(name_or_str, Name) and not name_context.is_instance():
|
||||
flow_scope = name_or_str
|
||||
base_nodes = [name_context.tree_node]
|
||||
|
||||
if any(b.type in ('comp_for', 'sync_comp_for') for b in base_nodes):
|
||||
return NO_VALUES
|
||||
from jedi.inference.finder import check_flow_information
|
||||
while True:
|
||||
flow_scope = get_parent_scope(flow_scope, include_flows=True)
|
||||
n = check_flow_information(name_context, flow_scope,
|
||||
name_or_str, position)
|
||||
if n is not None:
|
||||
return n
|
||||
if flow_scope in base_nodes:
|
||||
break
|
||||
return NO_VALUES
|
||||
|
||||
def get_root_context(self):
|
||||
parent_context = self.parent_context
|
||||
if parent_context is None:
|
||||
return self
|
||||
return parent_context.get_root_context()
|
||||
|
||||
def is_module(self):
|
||||
return False
|
||||
|
||||
def is_builtins_module(self):
|
||||
return False
|
||||
|
||||
def is_class(self):
|
||||
return False
|
||||
|
||||
def is_stub(self):
|
||||
return False
|
||||
|
||||
def is_instance(self):
|
||||
return False
|
||||
|
||||
def is_compiled(self):
|
||||
return False
|
||||
|
||||
def is_bound_method(self):
|
||||
return False
|
||||
|
||||
@abstractmethod
|
||||
def py__name__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_value(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return None
|
||||
|
||||
def get_qualified_names(self):
|
||||
return ()
|
||||
|
||||
def py__doc__(self):
|
||||
return ''
|
||||
|
||||
@contextmanager
|
||||
def predefine_names(self, flow_scope, dct):
|
||||
predefined = self.predefined_names
|
||||
predefined[flow_scope] = dct
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
del predefined[flow_scope]
|
||||
|
||||
|
||||
class ValueContext(AbstractContext):
|
||||
"""
|
||||
Should be defined, otherwise the API returns empty types.
|
||||
"""
|
||||
def __init__(self, value):
|
||||
super().__init__(value.inference_state)
|
||||
self._value = value
|
||||
|
||||
@property
|
||||
def tree_node(self):
|
||||
return self._value.tree_node
|
||||
|
||||
@property
|
||||
def parent_context(self):
|
||||
return self._value.parent_context
|
||||
|
||||
def is_module(self):
|
||||
return self._value.is_module()
|
||||
|
||||
def is_builtins_module(self):
|
||||
return self._value == self.inference_state.builtins_module
|
||||
|
||||
def is_class(self):
|
||||
return self._value.is_class()
|
||||
|
||||
def is_stub(self):
|
||||
return self._value.is_stub()
|
||||
|
||||
def is_instance(self):
|
||||
return self._value.is_instance()
|
||||
|
||||
def is_compiled(self):
|
||||
return self._value.is_compiled()
|
||||
|
||||
def is_bound_method(self):
|
||||
return self._value.is_bound_method()
|
||||
|
||||
def py__name__(self):
|
||||
return self._value.py__name__()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._value.name
|
||||
|
||||
def get_qualified_names(self):
|
||||
return self._value.get_qualified_names()
|
||||
|
||||
def py__doc__(self):
|
||||
return self._value.py__doc__()
|
||||
|
||||
def get_value(self):
|
||||
return self._value
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (self.__class__.__name__, self._value)
|
||||
|
||||
|
||||
class TreeContextMixin:
|
||||
def infer_node(self, node):
|
||||
from jedi.inference.syntax_tree import infer_node
|
||||
return infer_node(self, node)
|
||||
|
||||
def create_value(self, node):
|
||||
from jedi.inference import value
|
||||
|
||||
if node == self.tree_node:
|
||||
assert self.is_module()
|
||||
return self.get_value()
|
||||
|
||||
parent_context = self.create_context(node)
|
||||
|
||||
if node.type in ('funcdef', 'lambdef'):
|
||||
func = value.FunctionValue.from_context(parent_context, node)
|
||||
if parent_context.is_class():
|
||||
class_value = parent_context.parent_context.create_value(parent_context.tree_node)
|
||||
instance = value.AnonymousInstance(
|
||||
self.inference_state, parent_context.parent_context, class_value)
|
||||
func = value.BoundMethod(
|
||||
instance=instance,
|
||||
class_context=class_value.as_context(),
|
||||
function=func
|
||||
)
|
||||
return func
|
||||
elif node.type == 'classdef':
|
||||
return value.ClassValue(self.inference_state, parent_context, node)
|
||||
else:
|
||||
raise NotImplementedError("Probably shouldn't happen: %s" % node)
|
||||
|
||||
def create_context(self, node):
|
||||
def from_scope_node(scope_node, is_nested=True):
|
||||
if scope_node == self.tree_node:
|
||||
return self
|
||||
|
||||
if scope_node.type in ('funcdef', 'lambdef', 'classdef'):
|
||||
return self.create_value(scope_node).as_context()
|
||||
elif scope_node.type in ('comp_for', 'sync_comp_for'):
|
||||
parent_context = from_scope_node(parent_scope(scope_node.parent))
|
||||
if node.start_pos >= scope_node.children[-1].start_pos:
|
||||
return parent_context
|
||||
return CompForContext(parent_context, scope_node)
|
||||
raise Exception("There's a scope that was not managed: %s" % scope_node)
|
||||
|
||||
def parent_scope(node):
|
||||
while True:
|
||||
node = node.parent
|
||||
|
||||
if parser_utils.is_scope(node):
|
||||
return node
|
||||
elif node.type in ('argument', 'testlist_comp'):
|
||||
if node.children[1].type in ('comp_for', 'sync_comp_for'):
|
||||
return node.children[1]
|
||||
elif node.type == 'dictorsetmaker':
|
||||
for n in node.children[1:4]:
|
||||
# In dictionaries it can be pretty much anything.
|
||||
if n.type in ('comp_for', 'sync_comp_for'):
|
||||
return n
|
||||
|
||||
scope_node = parent_scope(node)
|
||||
if scope_node.type in ('funcdef', 'classdef'):
|
||||
colon = scope_node.children[scope_node.children.index(':')]
|
||||
if node.start_pos < colon.start_pos:
|
||||
parent = node.parent
|
||||
if not (parent.type == 'param' and parent.name == node):
|
||||
scope_node = parent_scope(scope_node)
|
||||
return from_scope_node(scope_node, is_nested=True)
|
||||
|
||||
def create_name(self, tree_name):
|
||||
definition = tree_name.get_definition()
|
||||
if definition and definition.type == 'param' and definition.name == tree_name:
|
||||
funcdef = search_ancestor(definition, 'funcdef', 'lambdef')
|
||||
func = self.create_value(funcdef)
|
||||
return AnonymousParamName(func, tree_name)
|
||||
else:
|
||||
context = self.create_context(tree_name)
|
||||
return TreeNameDefinition(context, tree_name)
|
||||
|
||||
|
||||
class FunctionContext(TreeContextMixin, ValueContext):
|
||||
def get_filters(self, until_position=None, origin_scope=None):
|
||||
yield ParserTreeFilter(
|
||||
self.inference_state,
|
||||
parent_context=self,
|
||||
until_position=until_position,
|
||||
origin_scope=origin_scope
|
||||
)
|
||||
|
||||
|
||||
class ModuleContext(TreeContextMixin, ValueContext):
|
||||
def py__file__(self) -> Optional[Path]:
|
||||
return self._value.py__file__() # type: ignore[no-any-return]
|
||||
|
||||
def get_filters(self, until_position=None, origin_scope=None):
|
||||
filters = self._value.get_filters(origin_scope)
|
||||
# Skip the first filter and replace it.
|
||||
next(filters, None)
|
||||
yield MergedFilter(
|
||||
ParserTreeFilter(
|
||||
parent_context=self,
|
||||
until_position=until_position,
|
||||
origin_scope=origin_scope
|
||||
),
|
||||
self.get_global_filter(),
|
||||
)
|
||||
yield from filters
|
||||
|
||||
def get_global_filter(self):
|
||||
return GlobalNameFilter(self)
|
||||
|
||||
@property
|
||||
def string_names(self):
|
||||
return self._value.string_names
|
||||
|
||||
@property
|
||||
def code_lines(self):
|
||||
return self._value.code_lines
|
||||
|
||||
def get_value(self):
|
||||
"""
|
||||
This is the only function that converts a context back to a value.
|
||||
This is necessary for stub -> python conversion and vice versa. However
|
||||
this method shouldn't be moved to AbstractContext.
|
||||
"""
|
||||
return self._value
|
||||
|
||||
|
||||
class NamespaceContext(TreeContextMixin, ValueContext):
|
||||
def get_filters(self, until_position=None, origin_scope=None):
|
||||
return self._value.get_filters()
|
||||
|
||||
def get_value(self):
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def string_names(self):
|
||||
return self._value.string_names
|
||||
|
||||
def py__file__(self) -> Optional[Path]:
|
||||
return self._value.py__file__() # type: ignore[no-any-return]
|
||||
|
||||
|
||||
class ClassContext(TreeContextMixin, ValueContext):
|
||||
def get_filters(self, until_position=None, origin_scope=None):
|
||||
yield self.get_global_filter(until_position, origin_scope)
|
||||
|
||||
def get_global_filter(self, until_position=None, origin_scope=None):
|
||||
return ParserTreeFilter(
|
||||
parent_context=self,
|
||||
until_position=until_position,
|
||||
origin_scope=origin_scope
|
||||
)
|
||||
|
||||
|
||||
class CompForContext(TreeContextMixin, AbstractContext):
|
||||
def __init__(self, parent_context, comp_for):
|
||||
super().__init__(parent_context.inference_state)
|
||||
self.tree_node = comp_for
|
||||
self.parent_context = parent_context
|
||||
|
||||
def get_filters(self, until_position=None, origin_scope=None):
|
||||
yield ParserTreeFilter(self)
|
||||
|
||||
def get_value(self):
|
||||
return None
|
||||
|
||||
def py__name__(self):
|
||||
return '<comprehension context>'
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (self.__class__.__name__, self.tree_node)
|
||||
|
||||
|
||||
class CompiledContext(ValueContext):
|
||||
def get_filters(self, until_position=None, origin_scope=None):
|
||||
return self._value.get_filters()
|
||||
|
||||
|
||||
class CompiledModuleContext(CompiledContext):
|
||||
code_lines = None
|
||||
|
||||
def get_value(self):
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def string_names(self):
|
||||
return self._value.string_names
|
||||
|
||||
def py__file__(self) -> Optional[Path]:
|
||||
return self._value.py__file__() # type: ignore[no-any-return]
|
||||
|
||||
|
||||
def _get_global_filters_for_name(context, name_or_none, position):
|
||||
# For functions and classes the defaults don't belong to the
|
||||
# function and get inferred in the value before the function. So
|
||||
# make sure to exclude the function/class name.
|
||||
if name_or_none is not None:
|
||||
ancestor = search_ancestor(name_or_none, 'funcdef', 'classdef', 'lambdef')
|
||||
lambdef = None
|
||||
if ancestor == 'lambdef':
|
||||
# For lambdas it's even more complicated since parts will
|
||||
# be inferred later.
|
||||
lambdef = ancestor
|
||||
ancestor = search_ancestor(name_or_none, 'funcdef', 'classdef')
|
||||
if ancestor is not None:
|
||||
colon = ancestor.children[-2]
|
||||
if position is not None and position < colon.start_pos:
|
||||
if lambdef is None or position < lambdef.children[-2].start_pos:
|
||||
position = ancestor.start_pos
|
||||
|
||||
return get_global_filters(context, position, name_or_none)
|
||||
|
||||
|
||||
def get_global_filters(context, until_position, origin_scope):
|
||||
"""
|
||||
Returns all filters in order of priority for name resolution.
|
||||
|
||||
For global name lookups. The filters will handle name resolution
|
||||
themselves, but here we gather possible filters downwards.
|
||||
|
||||
>>> from jedi import Script
|
||||
>>> script = Script('''
|
||||
... x = ['a', 'b', 'c']
|
||||
... def func():
|
||||
... y = None
|
||||
... ''')
|
||||
>>> module_node = script._module_node
|
||||
>>> scope = next(module_node.iter_funcdefs())
|
||||
>>> scope
|
||||
<Function: func@3-5>
|
||||
>>> context = script._get_module_context().create_context(scope)
|
||||
>>> filters = list(get_global_filters(context, (4, 0), None))
|
||||
|
||||
First we get the names from the function scope.
|
||||
|
||||
>>> print(filters[0]) # doctest: +ELLIPSIS
|
||||
MergedFilter(<ParserTreeFilter: ...>, <GlobalNameFilter: ...>)
|
||||
>>> sorted(str(n) for n in filters[0].values()) # doctest: +NORMALIZE_WHITESPACE
|
||||
['<TreeNameDefinition: string_name=func start_pos=(3, 4)>',
|
||||
'<TreeNameDefinition: string_name=x start_pos=(2, 0)>']
|
||||
>>> filters[0]._filters[0]._until_position
|
||||
(4, 0)
|
||||
>>> filters[0]._filters[1]._until_position
|
||||
|
||||
Then it yields the names from one level "lower". In this example, this is
|
||||
the module scope (including globals).
|
||||
As a side note, you can see, that the position in the filter is None on the
|
||||
globals filter, because there the whole module is searched.
|
||||
|
||||
>>> list(filters[1].values()) # package modules -> Also empty.
|
||||
[]
|
||||
>>> sorted(name.string_name for name in filters[2].values()) # Module attributes
|
||||
['__doc__', '__name__', '__package__']
|
||||
|
||||
Finally, it yields the builtin filter, if `include_builtin` is
|
||||
true (default).
|
||||
|
||||
>>> list(filters[3].values()) # doctest: +ELLIPSIS
|
||||
[...]
|
||||
"""
|
||||
base_context = context
|
||||
from jedi.inference.value.function import BaseFunctionExecutionContext
|
||||
while context is not None:
|
||||
# Names in methods cannot be resolved within the class.
|
||||
yield from context.get_filters(
|
||||
until_position=until_position,
|
||||
origin_scope=origin_scope
|
||||
)
|
||||
if isinstance(context, (BaseFunctionExecutionContext, ModuleContext)):
|
||||
# The position should be reset if the current scope is a function.
|
||||
until_position = None
|
||||
|
||||
context = context.parent_context
|
||||
|
||||
b = next(base_context.inference_state.builtins_module.get_filters(), None)
|
||||
assert b is not None
|
||||
# Add builtins to the global scope.
|
||||
yield b
|
||||
Loading…
Add table
Add a link
Reference in a new issue