mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-22 05:11:25 +08:00
150 lines
4.8 KiB
Python
150 lines
4.8 KiB
Python
"""
|
|
Filters are objects that you can use to filter names in different scopes. They
|
|
are needed for name resolution.
|
|
"""
|
|
from abc import abstractmethod
|
|
|
|
from jedi.parser.tree import search_ancestor
|
|
from jedi.evaluate import flow_analysis
|
|
from jedi.common import to_list
|
|
|
|
|
|
class AbstractFilter(object):
|
|
_until_position = None
|
|
|
|
def __init__(self, origin_scope=None):
|
|
self._origin_scope = origin_scope
|
|
|
|
def _filter(self, names):
|
|
if self._until_position is not None:
|
|
return [n for n in names if n.start_pos < self._until_position]
|
|
return names
|
|
|
|
@abstractmethod
|
|
def get(self, name):
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def values(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
class AbstractUsedNamesFilter(AbstractFilter):
|
|
def __init__(self, parser_scope, origin_scope=None):
|
|
super(AbstractUsedNamesFilter, self).__init__(origin_scope)
|
|
self._parser_scope = parser_scope
|
|
self._used_names = self._parser_scope.get_root_node().used_names
|
|
|
|
def get(self, name):
|
|
try:
|
|
names = self._used_names[str(name)]
|
|
except KeyError:
|
|
return []
|
|
|
|
return list(self._filter(names))
|
|
|
|
def values(self):
|
|
return [name for name_list in self._used_names.values()
|
|
for name in self._filter(name_list)]
|
|
|
|
|
|
class ParserTreeFilter(AbstractUsedNamesFilter):
|
|
def __init__(self, evaluator, parser_scope, until_position=None, origin_scope=None):
|
|
super(ParserTreeFilter, self).__init__(parser_scope, origin_scope)
|
|
self._until_position = until_position
|
|
self._evaluator = evaluator
|
|
|
|
def _filter(self, names):
|
|
names = super(ParserTreeFilter, self)._filter(names)
|
|
names = [n for n in names if n.is_definition()]
|
|
names = [n for n in names if n.parent.get_parent_scope() == self._parser_scope]
|
|
|
|
return list(self._check_flows(names))
|
|
|
|
def _check_flows(self, names):
|
|
for name in sorted(names, key=lambda name: name.start_pos, reverse=True):
|
|
stmt = name.get_definition()
|
|
name_scope = self._evaluator.wrap(stmt.get_parent_scope())
|
|
check = flow_analysis.break_check(self._evaluator, name_scope,
|
|
stmt, self._origin_scope)
|
|
if check is not flow_analysis.UNREACHABLE:
|
|
yield name
|
|
|
|
if check is flow_analysis.REACHABLE:
|
|
break
|
|
|
|
|
|
class FunctionExecutionFilter(ParserTreeFilter):
|
|
def __init__(self, evaluator, parser_scope, executed_function, param_by_name,
|
|
until_position=None, origin_scope=None):
|
|
super(FunctionExecutionFilter, self).__init__(
|
|
evaluator,
|
|
parser_scope,
|
|
until_position,
|
|
origin_scope
|
|
)
|
|
self._executed_function = executed_function
|
|
self._param_by_name = param_by_name
|
|
|
|
def _filter(self, names):
|
|
names = super(FunctionExecutionFilter, self)._filter(names)
|
|
|
|
names = [self._executed_function.name_for_position(name.start_pos) for name in names]
|
|
names = [self._param_by_name(str(name)) if search_ancestor(name, 'param') else name
|
|
for name in names]
|
|
return names
|
|
|
|
|
|
class GlobalNameFilter(AbstractUsedNamesFilter):
|
|
def __init__(self, parser_scope, origin_scope=None):
|
|
super(GlobalNameFilter, self).__init__(parser_scope)
|
|
|
|
@to_list
|
|
def _filter(self, names):
|
|
for name in names:
|
|
if name.parent.type == 'global_stmt':
|
|
yield name
|
|
|
|
|
|
class DictFilter(AbstractFilter):
|
|
def __init__(self, dct, origin_scope=None):
|
|
super(DictFilter, self).__init__(origin_scope)
|
|
self._dct = dct
|
|
|
|
def get(self, name):
|
|
try:
|
|
leaf_name = self._dct[str(name)]
|
|
except KeyError:
|
|
return []
|
|
|
|
return list(self._filter([leaf_name]))
|
|
|
|
def values(self):
|
|
return self._filter(self._dct.values())
|
|
|
|
|
|
def get_global_filters(evaluator, context, until_position, origin_scope):
|
|
"""
|
|
Returns all filters in order of priority for name resolution.
|
|
"""
|
|
in_func = False
|
|
while context is not None:
|
|
if not (context.type == 'classdef' and in_func):
|
|
# Names in methods cannot be resolved within the class.
|
|
for filter in context.get_filters(
|
|
search_global=True,
|
|
until_position=until_position,
|
|
origin_scope=origin_scope):
|
|
yield filter
|
|
if context.type == 'funcdef':
|
|
# The position should be reset if the current scope is a function.
|
|
until_position = None
|
|
in_func = True
|
|
|
|
node = context.get_parent_scope()
|
|
context = evaluator.wrap(node)
|
|
|
|
# Add builtins to the global scope.
|
|
for filter in evaluator.BUILTINS.get_filters(search_global=True):
|
|
yield filter
|