Skip to content

Commit

Permalink
Move get_global_filters to the context module
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhalter committed Aug 23, 2019
1 parent a9d8f38 commit 60a73f6
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 97 deletions.
11 changes: 5 additions & 6 deletions jedi/api/completion.py
Expand Up @@ -13,7 +13,7 @@
from jedi.api.file_name import file_name_completions
from jedi.inference import imports
from jedi.inference.helpers import infer_call_of_leaf, parse_dotted_names
from jedi.inference.filters import get_global_filters
from jedi.inference.context import get_global_filters
from jedi.inference.gradual.conversion import convert_values
from jedi.parser_utils import cut_value_at_position

Expand Down Expand Up @@ -222,14 +222,13 @@ def _get_keyword_completion_names(self, allowed_transitions):
yield keywords.KeywordName(self._inference_state, k)

def _global_completions(self):
value = get_user_context(self._module_context, self._position)
debug.dbg('global completion scope: %s', value)
context = get_user_context(self._module_context, self._position)
debug.dbg('global completion scope: %s', context)
flow_scope_node = get_flow_scope_node(self._module_node, self._position)
filters = get_global_filters(
self._inference_state,
value,
context,
self._position,
origin_scope=flow_scope_node
flow_scope_node
)
completion_names = []
for filter in filters:
Expand Down
98 changes: 96 additions & 2 deletions jedi/inference/context.py
@@ -1,5 +1,8 @@
from abc import abstractmethod

from parso.tree import search_ancestor
from parso.python.tree import Name

from jedi.inference.filters import ParserTreeFilter, MergedFilter, \
GlobalNameFilter
from jedi import parser_utils
Expand All @@ -19,7 +22,9 @@ def get_filters(self, until_position=None, origin_scope=None):
def goto(self, name_or_str, position):
from jedi.inference import finder
f = finder.NameFinder(self.inference_state, self, self, name_or_str, position)
filters = f.get_global_filters()
filters = _get_global_filters_for_name(
self, name_or_str if isinstance(name_or_str, Name) else None, position,
)
return f.filter_name(filters)

def py__getattribute__(self, name_or_str, name_value=None, position=None,
Expand All @@ -32,7 +37,9 @@ def py__getattribute__(self, name_or_str, name_value=None, position=None,
from jedi.inference import finder
f = finder.NameFinder(self.inference_state, self, name_value, name_or_str,
position, analysis_errors=analysis_errors)
filters = f.get_global_filters()
filters = _get_global_filters_for_name(
self, name_or_str if isinstance(name_or_str, Name) else None, position,
)
return f.find(filters, attribute_lookup=False)

def get_root_context(self):
Expand Down Expand Up @@ -290,3 +297,90 @@ def get_value(self):

def py__file__(self):
return self._value.py__file__()


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._compatibility import u, no_unicode_pprint
>>> from jedi import Script
>>> script = Script(u('''
... 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.
>>> no_unicode_pprint(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 FunctionExecutionContext
while context is not None:
# Names in methods cannot be resolved within the class.
for filter in context.get_filters(
until_position=until_position,
origin_scope=origin_scope):
yield filter
if isinstance(context, FunctionExecutionContext):
# The position should be reset if the current scope is a function.
until_position = None

context = context.parent_context

# Add builtins to the global scope.
yield next(base_context.inference_state.builtins_module.get_filters())
65 changes: 0 additions & 65 deletions jedi/inference/filters.py
Expand Up @@ -339,68 +339,3 @@ def decorator(func):
dct[method_name] = func, python_version_match
return func
return decorator


def get_global_filters(inference_state, 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._compatibility import u, no_unicode_pprint
>>> from jedi import Script
>>> script = Script(u('''
... 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.inference_state, context, (4, 0), None))
First we get the names from the function scope.
>>> no_unicode_pprint(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
[...]
"""
from jedi.inference.value.function import FunctionExecutionContext
while context is not None:
# Names in methods cannot be resolved within the class.
for filter in context.get_filters(
until_position=until_position,
origin_scope=origin_scope):
yield filter
if isinstance(context, FunctionExecutionContext):
# The position should be reset if the current scope is a function.
until_position = None

context = context.parent_context

# Add builtins to the global scope.
yield next(inference_state.builtins_module.get_filters())
24 changes: 0 additions & 24 deletions jedi/inference/finder.py
Expand Up @@ -25,7 +25,6 @@
from jedi.inference.arguments import TreeArguments
from jedi.inference import helpers
from jedi.inference.value import iterable
from jedi.inference.filters import get_global_filters
from jedi.inference.names import TreeNameDefinition
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.parser_utils import is_scope, get_parent_scope
Expand Down Expand Up @@ -79,29 +78,6 @@ def find(self, filters, attribute_lookup):

return types

def get_global_filters(self):
origin_scope = self._name if isinstance(self._name, tree.Name) else None
position = self._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 origin_scope is not None:
ancestor = search_ancestor(origin_scope, '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(origin_scope, '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(self._inference_state, self._context, position, origin_scope)

def filter_name(self, filters):
"""
Searches names that are defined in a scope (the different
Expand Down

0 comments on commit 60a73f6

Please sign in to comment.