Skip to content

Commit 619acbd

Browse files
committed
Goto didn't work well on imports in __init__.py files.
Fixes #956.
1 parent c05f1d3 commit 619acbd

File tree

10 files changed

+56
-13
lines changed

10 files changed

+56
-13
lines changed

jedi/api/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,23 @@ def goto_assignments(self, follow_imports=False):
223223
224224
:rtype: list of :class:`classes.Definition`
225225
"""
226-
def filter_follow_imports(names):
226+
def filter_follow_imports(names, follow_classes):
227227
for name in names:
228-
if isinstance(name, (imports.ImportName, TreeNameDefinition)):
228+
if isinstance(name, follow_classes):
229229
for context in name.infer():
230230
yield context.name
231231
else:
232232
yield name
233233

234234
names = self._goto()
235235
if follow_imports:
236-
names = filter_follow_imports(names)
236+
# TODO really, sure? TreeNameDefinition? Should probably not follow
237+
# that.
238+
follow_classes = (imports.ImportName, TreeNameDefinition)
239+
else:
240+
follow_classes = (imports.SubModuleName,)
241+
242+
names = filter_follow_imports(names, follow_classes)
237243

238244
defs = [classes.Definition(self._evaluator, d) for d in set(names)]
239245
return helpers.sorted_definitions(defs)

jedi/evaluate/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def reset_recursion_limitations(self):
118118
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
119119

120120
def find_types(self, context, name_or_str, name_context, position=None,
121-
search_global=False, is_goto=False):
121+
search_global=False, is_goto=False, analysis_errors=True):
122122
"""
123123
This is the search function. The most important part to debug.
124124
`remove_statements` and `filter_statements` really are the core part of
@@ -127,7 +127,8 @@ def find_types(self, context, name_or_str, name_context, position=None,
127127
:param position: Position of the last statement -> tuple of line, column
128128
:return: List of Names. Their parents are the types.
129129
"""
130-
f = finder.NameFinder(self, context, name_context, name_or_str, position)
130+
f = finder.NameFinder(self, context, name_context, name_or_str,
131+
position, analysis_errors=analysis_errors)
131132
filters = f.get_filters(search_global)
132133
if is_goto:
133134
return f.filter_name(filters)

jedi/evaluate/context.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ def eval_trailer(self, types, trailer):
5454

5555
@Python3Method
5656
def py__getattribute__(self, name_or_str, name_context=None, position=None,
57-
search_global=False, is_goto=False):
57+
search_global=False, is_goto=False,
58+
analysis_errors=True):
5859
if name_context is None:
5960
name_context = self
6061
return self.evaluator.find_types(
61-
self, name_or_str, name_context, position, search_global, is_goto)
62+
self, name_or_str, name_context, position, search_global, is_goto,
63+
analysis_errors)
6264

6365
def create_context(self, node, node_is_context=False, node_is_object=False):
6466
return self.evaluator.create_context(self, node, node_is_context, node_is_object)

jedi/evaluate/finder.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@
3030
from jedi.evaluate import flow_analysis
3131
from jedi.evaluate import param
3232
from jedi.evaluate import helpers
33-
from jedi.evaluate.filters import get_global_filters
33+
from jedi.evaluate.filters import get_global_filters, TreeNameDefinition
3434
from jedi.evaluate.context import ContextualizedName, ContextualizedNode
3535
from jedi.parser_utils import is_scope, get_parent_scope
3636

3737

3838
class NameFinder(object):
39-
def __init__(self, evaluator, context, name_context, name_or_str, position=None):
39+
def __init__(self, evaluator, context, name_context, name_or_str,
40+
position=None, analysis_errors=True):
4041
self._evaluator = evaluator
4142
# Make sure that it's not just a syntax tree node.
4243
self._context = context
@@ -48,6 +49,7 @@ def __init__(self, evaluator, context, name_context, name_or_str, position=None)
4849
self._string_name = name_or_str
4950
self._position = position
5051
self._found_predefined_types = None
52+
self._analysis_errors = analysis_errors
5153

5254
@debug.increase_indent
5355
def find(self, filters, attribute_lookup):
@@ -65,7 +67,7 @@ def find(self, filters, attribute_lookup):
6567

6668
types = self._names_to_types(names, attribute_lookup)
6769

68-
if not names and not types \
70+
if not names and self._analysis_errors and not types \
6971
and not (isinstance(self._name, tree.Name) and
7072
isinstance(self._name.parent.parent, tree.Param)):
7173
if isinstance(self._name, tree.Name):
@@ -122,7 +124,19 @@ def filter_name(self, filters):
122124
for filter in filters:
123125
names = filter.get(self._string_name)
124126
if names:
127+
if len(names) == 1:
128+
n, = names
129+
if isinstance(n, TreeNameDefinition):
130+
# Something somewhere went terribly wrong. This
131+
# typically happens when using goto on an import in an
132+
# __init__ file. I think we need a better solution, but
133+
# it's kind of hard, because for Jedi it's not clear
134+
# that that name has not been defined, yet.
135+
if n.tree_name == self._name:
136+
if self._name.get_definition().type == 'import_from':
137+
continue
125138
break
139+
126140
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self._string_name,
127141
self._context, names, self._position)
128142
return list(names)

jedi/evaluate/imports.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ def infer_import(context, tree_name, is_goto=False):
6565
if from_import_name is not None:
6666
types = unite(
6767
t.py__getattribute__(
68-
from_import_name.value if isinstance(from_import_name, tree.Name) else from_import_name,
68+
from_import_name,
6969
name_context=context,
70-
is_goto=is_goto
70+
is_goto=is_goto,
71+
analysis_errors=False
7172
) for t in types
7273
)
7374

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
addopts = --doctest-modules
33

44
# Ignore broken files in blackbox test directories
5-
norecursedirs = .* docs completion refactor absolute_import namespace_package scripts extensions speed static_analysis not_in_sys_path buildout_project sample_venvs init_extension_module
5+
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
66

77
# Activate `clean_jedi_cache` fixture for all tests. This should be
88
# fine as long as we are using `clean_jedi_cache` as a session scoped
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from simple_import import module
2+
3+
4+
def in_function():
5+
from simple_import import module2

test/test_api/simple_import/module.py

Whitespace-only changes.

test/test_api/simple_import/module2.py

Whitespace-only changes.

test/test_api/test_api.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Test all things related to the ``jedi.api`` module.
33
"""
44

5+
import os
56
from textwrap import dedent
67

78
from jedi import api
@@ -205,3 +206,16 @@ def test_goto_assignments_follow_imports():
205206

206207
definition, = script.goto_assignments()
207208
assert (definition.line, definition.column) == start_pos
209+
210+
211+
def test_goto_module():
212+
def check(line, expected):
213+
script = api.Script(path=path, line=line)
214+
module, = script.goto_assignments()
215+
assert module.module_path == expected
216+
217+
base_path = os.path.join(os.path.dirname(__file__), 'simple_import')
218+
path = os.path.join(base_path, '__init__.py')
219+
220+
check(1, os.path.join(base_path, 'module.py'))
221+
check(5, os.path.join(base_path, 'module2.py'))

0 commit comments

Comments
 (0)