diff --git a/jedi/api.py b/jedi/api.py index 51c1071c..1ed062d3 100644 --- a/jedi/api.py +++ b/jedi/api.py @@ -271,7 +271,7 @@ class Script(object): d = set([api_classes.Definition(s) for s in scopes if not isinstance(s, imports.ImportPath._GlobalNamespace)]) - return sorted(d, key=lambda x: (x.module_path, x.start_pos)) + return self._sorted_defs(d) @api_classes._clear_caches_after_call def goto(self): @@ -284,7 +284,7 @@ class Script(object): :rtype: list of :class:`api_classes.Definition` """ d = [api_classes.Definition(d) for d in set(self._goto()[0])] - return sorted(d, key=lambda x: (x.module_path, x.start_pos)) + return self._sorted_defs(d) def _goto(self, add_import_name=False): """ @@ -370,7 +370,7 @@ class Script(object): else: names.append(api_classes.RelatedName(d.names[-1], d)) - return sorted(set(names), key=lambda x: (x.module_path, x.start_pos)) + return self._sorted_defs(set(names)) def get_in_function_call(self): """ @@ -491,6 +491,12 @@ class Script(object): match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path, flags=re.S) return match.groups() + @staticmethod + def _sorted_defs(d): + # Note: `or ''` below is required because `module_path` could be + # None and you can't compare None and str in Python 3. + return sorted(d, key=lambda x: (x.module_path or '', x.start_pos)) + def defined_names(source, source_path=None, source_encoding='utf-8'): """ diff --git a/jedi/api_classes.py b/jedi/api_classes.py index df63198d..eb6b58d7 100644 --- a/jedi/api_classes.py +++ b/jedi/api_classes.py @@ -120,20 +120,22 @@ class BaseDefinition(object): Finally, here is what you can get from :attr:`type`: >>> defs[0].type - 'Module' + 'module' >>> defs[1].type - 'Class' + 'class' >>> defs[2].type - 'Instance' + 'instance' >>> defs[3].type - 'Function' + 'function' """ # generate the type stripped = self.definition if isinstance(self.definition, er.InstanceElement): stripped = self.definition.var - return type(stripped).__name__ + if isinstance(stripped, pr.Name): + stripped = stripped.parent + return type(stripped).__name__.lower() @property def path(self): diff --git a/test/test_api_classes.py b/test/test_api_classes.py new file mode 100644 index 00000000..bcb7a722 --- /dev/null +++ b/test/test_api_classes.py @@ -0,0 +1,53 @@ +import textwrap + +import pytest + +from jedi import api + + +def make_definitions(): + """ + Return a list of definitions for parametrized tests. + + :rtype: [jedi.api_classes.BaseDefinition] + """ + source = textwrap.dedent(""" + import sys + + class C: + pass + + x = C() + + def f(): + pass + + def g(): + yield + + h = lambda: None + """) + + definitions = [] + definitions += api.defined_names(source) + + source += textwrap.dedent(""" + variable = sys or C or x or f or g or h""") + lines = source.splitlines() + script = api.Script(source, len(lines), len('variable'), None) + definitions += script.definition() + + script2 = api.Script(source, 4, len('class C'), None) + definitions += script2.related_names() + + source_param = "def f(a): return a" + script_param = api.Script(source_param, 1, len(source_param), None) + definitions += script_param.goto() + + return definitions + + +@pytest.mark.parametrize('definition', make_definitions()) +def test_basedefinition_type(definition): + assert definition.type in ('module', 'class', 'instance', 'function', + 'statement', 'import', 'param') diff --git a/test/test_regression.py b/test/test_regression.py index 6c4181f8..d71dec1a 100755 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -319,7 +319,7 @@ class TestRegression(TestBase): # attributes objs = itertools.chain.from_iterable(r.follow_definition() for r in c) types = [o.type for o in objs] - assert 'Import' not in types and 'Class' in types + assert 'import' not in types and 'class' in types def test_keyword_definition_doc(self): """ github jedi-vim issue #44 """