forked from VimPlug/jedi
@@ -223,9 +223,9 @@ class Script(object):
|
||||
|
||||
:rtype: list of :class:`classes.Definition`
|
||||
"""
|
||||
def filter_follow_imports(names):
|
||||
def filter_follow_imports(names, follow_classes):
|
||||
for name in names:
|
||||
if isinstance(name, (imports.ImportName, TreeNameDefinition)):
|
||||
if isinstance(name, follow_classes):
|
||||
for context in name.infer():
|
||||
yield context.name
|
||||
else:
|
||||
@@ -233,7 +233,13 @@ class Script(object):
|
||||
|
||||
names = self._goto()
|
||||
if follow_imports:
|
||||
names = filter_follow_imports(names)
|
||||
# TODO really, sure? TreeNameDefinition? Should probably not follow
|
||||
# that.
|
||||
follow_classes = (imports.ImportName, TreeNameDefinition)
|
||||
else:
|
||||
follow_classes = (imports.SubModuleName,)
|
||||
|
||||
names = filter_follow_imports(names, follow_classes)
|
||||
|
||||
defs = [classes.Definition(self._evaluator, d) for d in set(names)]
|
||||
return helpers.sorted_definitions(defs)
|
||||
|
||||
@@ -118,7 +118,7 @@ class Evaluator(object):
|
||||
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
|
||||
|
||||
def find_types(self, context, name_or_str, name_context, position=None,
|
||||
search_global=False, is_goto=False):
|
||||
search_global=False, is_goto=False, analysis_errors=True):
|
||||
"""
|
||||
This is the search function. The most important part to debug.
|
||||
`remove_statements` and `filter_statements` really are the core part of
|
||||
@@ -127,7 +127,8 @@ class Evaluator(object):
|
||||
:param position: Position of the last statement -> tuple of line, column
|
||||
:return: List of Names. Their parents are the types.
|
||||
"""
|
||||
f = finder.NameFinder(self, context, name_context, name_or_str, position)
|
||||
f = finder.NameFinder(self, context, name_context, name_or_str,
|
||||
position, analysis_errors=analysis_errors)
|
||||
filters = f.get_filters(search_global)
|
||||
if is_goto:
|
||||
return f.filter_name(filters)
|
||||
|
||||
@@ -54,11 +54,13 @@ class Context(object):
|
||||
|
||||
@Python3Method
|
||||
def py__getattribute__(self, name_or_str, name_context=None, position=None,
|
||||
search_global=False, is_goto=False):
|
||||
search_global=False, is_goto=False,
|
||||
analysis_errors=True):
|
||||
if name_context is None:
|
||||
name_context = self
|
||||
return self.evaluator.find_types(
|
||||
self, name_or_str, name_context, position, search_global, is_goto)
|
||||
self, name_or_str, name_context, position, search_global, is_goto,
|
||||
analysis_errors)
|
||||
|
||||
def create_context(self, node, node_is_context=False, node_is_object=False):
|
||||
return self.evaluator.create_context(self, node, node_is_context, node_is_object)
|
||||
|
||||
@@ -30,13 +30,14 @@ from jedi.evaluate import analysis
|
||||
from jedi.evaluate import flow_analysis
|
||||
from jedi.evaluate import param
|
||||
from jedi.evaluate import helpers
|
||||
from jedi.evaluate.filters import get_global_filters
|
||||
from jedi.evaluate.filters import get_global_filters, TreeNameDefinition
|
||||
from jedi.evaluate.context import ContextualizedName, ContextualizedNode
|
||||
from jedi.parser_utils import is_scope, get_parent_scope
|
||||
|
||||
|
||||
class NameFinder(object):
|
||||
def __init__(self, evaluator, context, name_context, name_or_str, position=None):
|
||||
def __init__(self, evaluator, context, name_context, name_or_str,
|
||||
position=None, analysis_errors=True):
|
||||
self._evaluator = evaluator
|
||||
# Make sure that it's not just a syntax tree node.
|
||||
self._context = context
|
||||
@@ -48,6 +49,7 @@ class NameFinder(object):
|
||||
self._string_name = name_or_str
|
||||
self._position = position
|
||||
self._found_predefined_types = None
|
||||
self._analysis_errors = analysis_errors
|
||||
|
||||
@debug.increase_indent
|
||||
def find(self, filters, attribute_lookup):
|
||||
@@ -65,7 +67,7 @@ class NameFinder(object):
|
||||
|
||||
types = self._names_to_types(names, attribute_lookup)
|
||||
|
||||
if not names and not types \
|
||||
if not names and self._analysis_errors and not types \
|
||||
and not (isinstance(self._name, tree.Name) and
|
||||
isinstance(self._name.parent.parent, tree.Param)):
|
||||
if isinstance(self._name, tree.Name):
|
||||
@@ -122,7 +124,19 @@ class NameFinder(object):
|
||||
for filter in filters:
|
||||
names = filter.get(self._string_name)
|
||||
if names:
|
||||
if len(names) == 1:
|
||||
n, = names
|
||||
if isinstance(n, TreeNameDefinition):
|
||||
# Something somewhere went terribly wrong. This
|
||||
# typically happens when using goto on an import in an
|
||||
# __init__ file. I think we need a better solution, but
|
||||
# it's kind of hard, because for Jedi it's not clear
|
||||
# that that name has not been defined, yet.
|
||||
if n.tree_name == self._name:
|
||||
if self._name.get_definition().type == 'import_from':
|
||||
continue
|
||||
break
|
||||
|
||||
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self._string_name,
|
||||
self._context, names, self._position)
|
||||
return list(names)
|
||||
|
||||
@@ -65,9 +65,10 @@ def infer_import(context, tree_name, is_goto=False):
|
||||
if from_import_name is not None:
|
||||
types = unite(
|
||||
t.py__getattribute__(
|
||||
from_import_name.value if isinstance(from_import_name, tree.Name) else from_import_name,
|
||||
from_import_name,
|
||||
name_context=context,
|
||||
is_goto=is_goto
|
||||
is_goto=is_goto,
|
||||
analysis_errors=False
|
||||
) for t in types
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
addopts = --doctest-modules
|
||||
|
||||
# Ignore broken files in blackbox test directories
|
||||
norecursedirs = .* docs completion refactor absolute_import namespace_package scripts extensions speed static_analysis not_in_sys_path buildout_project sample_venvs init_extension_module
|
||||
norecursedirs = .* docs completion refactor absolute_import namespace_package scripts extensions speed static_analysis not_in_sys_path buildout_project sample_venvs init_extension_module simple_import
|
||||
|
||||
# Activate `clean_jedi_cache` fixture for all tests. This should be
|
||||
# fine as long as we are using `clean_jedi_cache` as a session scoped
|
||||
|
||||
5
test/test_api/simple_import/__init__.py
Normal file
5
test/test_api/simple_import/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from simple_import import module
|
||||
|
||||
|
||||
def in_function():
|
||||
from simple_import import module2
|
||||
0
test/test_api/simple_import/module.py
Normal file
0
test/test_api/simple_import/module.py
Normal file
0
test/test_api/simple_import/module2.py
Normal file
0
test/test_api/simple_import/module2.py
Normal file
@@ -2,6 +2,7 @@
|
||||
Test all things related to the ``jedi.api`` module.
|
||||
"""
|
||||
|
||||
import os
|
||||
from textwrap import dedent
|
||||
|
||||
from jedi import api
|
||||
@@ -205,3 +206,16 @@ def test_goto_assignments_follow_imports():
|
||||
|
||||
definition, = script.goto_assignments()
|
||||
assert (definition.line, definition.column) == start_pos
|
||||
|
||||
|
||||
def test_goto_module():
|
||||
def check(line, expected):
|
||||
script = api.Script(path=path, line=line)
|
||||
module, = script.goto_assignments()
|
||||
assert module.module_path == expected
|
||||
|
||||
base_path = os.path.join(os.path.dirname(__file__), 'simple_import')
|
||||
path = os.path.join(base_path, '__init__.py')
|
||||
|
||||
check(1, os.path.join(base_path, 'module.py'))
|
||||
check(5, os.path.join(base_path, 'module2.py'))
|
||||
|
||||
Reference in New Issue
Block a user