diff --git a/jedi/api/classes.py b/jedi/api/classes.py index 4063bfcc..54d5bc42 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -148,6 +148,18 @@ class BaseDefinition(object): 'function' """ + tree_name = self._name.tree_name + resolve = False + if tree_name is not None: + # TODO move this to their respective names. + definition = tree_name.get_definition() + if definition.type == 'import_from' and \ + tree_name in definition.get_defined_names(): + resolve = True + + if isinstance(self._name, imports.SubModuleName) or resolve: + for context in self._name.infer(): + return context.api_type return self._name.api_type def _path(self): @@ -353,7 +365,15 @@ class BaseDefinition(object): return [_Param(self._evaluator, n) for n in get_param_names(context)] def parent(self): - return Definition(self._evaluator, self._name.parent_context.name) + context = self._name.parent_context + if context is None: + return None + + if isinstance(context, er.FunctionExecutionContext): + # TODO the function context should be a part of the function + # execution context. + context = er.FunctionContext(self._evaluator, context.parent_context, context.funcdef) + return Definition(self._evaluator, context.name) def __repr__(self): return "<%s %s>" % (type(self).__name__, self.description) @@ -475,17 +495,6 @@ class Completion(BaseDefinition): else: return _Help(context).full() - @property - def type(self): - """ - The type of the completion objects. Follows imports. For a further - description, look at :attr:`jedi.api.classes.BaseDefinition.type`. - """ - if self._name.api_type == 'module': - for context in self._name.infer(): - return context.name.api_type - return super(Completion, self).type - @memoize_method def _follow_statements_imports(self): # imports completion is very complicated and needs to be treated @@ -586,7 +595,7 @@ class Definition(BaseDefinition): d = self._definition if isinstance(d, compiled.CompiledObject): - typ = d.api_type() + typ = d.api_type if typ == 'instance': typ = 'class' # The description should be similar to Py objects. d = typ + ' ' + d.name.get_code() diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 68e68701..38fb6a88 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -488,6 +488,8 @@ class Evaluator(object): # a name it's something you can "goto" again. return [TreeNameDefinition(context, name)] elif isinstance(par, (tree.Param, tree.Function, tree.Class)) and par.name is name: + if par.type in ('funcdef', 'classdef', 'module'): + return [context.name] return [TreeNameDefinition(context, name)] elif isinstance(stmt, tree.Import): module_names = imports.ImportWrapper(context, name).follow(is_goto=True) diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index b534da21..c86c095f 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -305,10 +305,6 @@ class CompiledContextName(AbstractNameDefinition): self.string_name = name self.parent_context = parent_context - @property - def api_type(self): - return self.parent_context.api_type - def infer(self): return [self.parent_context] diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index dcea70b7..ff87c5ac 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -13,14 +13,13 @@ class AbstractNameDefinition(object): start_pos = None string_name = None parent_context = None + tree_name = None @abstractmethod def infer(self): raise NotImplementedError def get_root_context(self): - if self.parent_context is None: - return self return self.parent_context.get_root_context() def __repr__(self): @@ -34,8 +33,12 @@ class AbstractNameDefinition(object): def execute_evaluated(self, *args, **kwargs): return unite(context.execute_evaluated(*args, **kwargs) for context in self.infer()) + @property + def api_type(self): + return self.parent_context.api_type -class ContextName(AbstractNameDefinition): + +class AbstractTreeName(AbstractNameDefinition): def __init__(self, parent_context, tree_name): self.parent_context = parent_context self.tree_name = tree_name @@ -48,15 +51,26 @@ class ContextName(AbstractNameDefinition): def start_pos(self): return self.tree_name.start_pos + +class ContextName(AbstractTreeName): + def __init__(self, context, tree_name): + super(ContextName, self).__init__(context.parent_context, tree_name) + self._context = context + def infer(self): - return [self.parent_context] + return [self._context] + + def get_root_context(self): + if self.parent_context is None: + return self._context + return super(ContextName, self).get_root_context() @property def api_type(self): - return self.parent_context.api_type + return self._context.api_type -class TreeNameDefinition(ContextName): +class TreeNameDefinition(AbstractTreeName): def get_parent_flow_context(self): return self.parent_context @@ -77,7 +91,7 @@ class TreeNameDefinition(ContextName): ).get(definition.type, 'statement') -class ParamName(ContextName): +class ParamName(AbstractTreeName): api_type = 'param' def __init__(self, parent_context, tree_name): diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index be035066..d0c78a06 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -166,7 +166,6 @@ def get_init_path(directory_path): class ImportName(AbstractNameDefinition): - api_type = 'module' start_pos = (1, 0) def __init__(self, parent_module, string_name): @@ -188,6 +187,9 @@ class ImportName(AbstractNameDefinition): def parent_context(self): return self.parent_module + @property + def api_type(self): + return 'module' class SubModuleName(ImportName): def infer(self): diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index bad1ae85..4a6bd13b 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -520,13 +520,16 @@ class BaseNode(Base): def end_pos(self): return self.children[-1].end_pos - def get_code(self, normalized=False, include_prefix=True): + def _get_code_for_children(self, children, normalized, include_prefix): # TODO implement normalized (depending on context). if include_prefix: - return "".join(c.get_code(normalized) for c in self.children) + return "".join(c.get_code(normalized) for c in children) else: - first = self.children[0].get_code(include_prefix=False) - return first + "".join(c.get_code(normalized) for c in self.children[1:]) + first = children[0].get_code(include_prefix=False) + return first + "".join(c.get_code(normalized) for c in children[1:]) + + def get_code(self, normalized=False, include_prefix=True): + return self._get_code_for_children(self.children, normalized, include_prefix) @Python3Method def name_for_position(self, position): @@ -1621,6 +1624,12 @@ class Param(BaseNode): default = '' if self.default is None else '=%s' % self.default.get_code() return '<%s: %s>' % (type(self).__name__, str(self._tfpdef()) + default) + def get_code(self, normalized=False, include_prefix=True): + children = self.children + if children[-1] == ',': + children = children[:-1] + return self._get_code_for_children(children, normalized, include_prefix) + class CompFor(BaseNode): type = 'comp_for' diff --git a/test/test_api/test_classes.py b/test/test_api/test_classes.py index 64d92648..170c1951 100644 --- a/test/test_api/test_classes.py +++ b/test/test_api/test_classes.py @@ -164,7 +164,7 @@ def test_param_endings(): around them. """ sig = Script('def x(a, b=5, c=""): pass\n x(').call_signatures()[0] - assert [p.description for p in sig.params] == ['a', 'b=5', 'c=""'] + assert [p.description for p in sig.params] == ['param a', 'param b=5', 'param c=""'] class TestIsDefinition(TestCase): @@ -202,9 +202,8 @@ class TestIsDefinition(TestCase): class TestParent(TestCase): def _parent(self, source, line=None, column=None): - defs = Script(dedent(source), line, column).goto_assignments() - assert len(defs) == 1 - return defs[0].parent() + def_, = Script(dedent(source), line, column).goto_assignments() + return def_.parent() def test_parent(self): parent = self._parent('foo=1\nfoo') @@ -231,7 +230,7 @@ class TestParent(TestCase): def bar(): pass Foo().bar''')).completions()[0].parent() assert parent.name == 'Foo' - assert parent.type == 'instance' + assert parent.type == 'class' parent = Script('str.join').completions()[0].parent() assert parent.name == 'str' @@ -287,27 +286,27 @@ class TestGotoAssignments(TestCase): def test_import(self): nms = names('from json import load', references=True) assert nms[0].name == 'json' - assert nms[0].type == 'import' + assert nms[0].type == 'module' n = nms[0].goto_assignments()[0] assert n.name == 'json' assert n.type == 'module' assert nms[1].name == 'load' - assert nms[1].type == 'import' + assert nms[1].type == 'function' n = nms[1].goto_assignments()[0] assert n.name == 'load' assert n.type == 'function' nms = names('import os; os.path', references=True) assert nms[0].name == 'os' - assert nms[0].type == 'import' + assert nms[0].type == 'module' n = nms[0].goto_assignments()[0] assert n.name == 'os' assert n.type == 'module' n = nms[2].goto_assignments()[0] assert n.name == 'path' - assert n.type == 'import' + assert n.type == 'module' nms = names('import os.path', references=True) n = nms[0].goto_assignments()[0] @@ -322,17 +321,21 @@ class TestGotoAssignments(TestCase): def test_import_alias(self): nms = names('import json as foo', references=True) assert nms[0].name == 'json' - assert nms[0].type == 'import' + assert nms[0].type == 'module' + assert nms[0]._name.tree_name.get_definition().type == 'import_name' n = nms[0].goto_assignments()[0] assert n.name == 'json' assert n.type == 'module' + assert n._name.tree_name.get_definition().type == 'file_input' assert nms[1].name == 'foo' - assert nms[1].type == 'import' + assert nms[1].type == 'module' + assert nms[1]._name.tree_name.get_definition().type == 'import_name' ass = nms[1].goto_assignments() assert len(ass) == 1 assert ass[0].name == 'json' assert ass[0].type == 'module' + assert ass[0]._name.tree_name.get_definition().type == 'file_input' def test_added_equals_to_params():