diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index a5da3c54..1b62ee23 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -378,7 +378,7 @@ class Interpreter(Script): def names(source=None, path=None, encoding='utf-8', all_scopes=False, - definitions=True, references=False): + definitions=True, references=False, environment=None): """ Returns a list of `Definition` objects, containing name parts. This means you can call ``Definition.goto_assignments()`` and get the @@ -398,7 +398,7 @@ def names(source=None, path=None, encoding='utf-8', all_scopes=False, return definitions and is_def or references and not is_def # Set line/column to a random position, because they don't matter. - script = Script(source, line=1, column=0, path=path, encoding=encoding) + script = Script(source, line=1, column=0, path=path, encoding=encoding, environment=environment) module_context = script._get_module() defs = [ classes.Definition( diff --git a/test/test_api/test_analysis.py b/test/test_api/test_analysis.py index cfcd405d..f0b6a449 100644 --- a/test/test_api/test_analysis.py +++ b/test/test_api/test_analysis.py @@ -1,10 +1,9 @@ """ Test of keywords and ``jedi.keywords`` """ -from jedi import Script -def test_issue436(): +def test_issue436(Script): code = "bar = 0\nbar += 'foo' + 4" errors = set(repr(e) for e in Script(code)._analysis()) assert len(errors) == 2 diff --git a/test/test_api/test_api_classes_follow_definition.py b/test/test_api/test_api_classes_follow_definition.py index ffc0cb64..e8342036 100644 --- a/test/test_api/test_api_classes_follow_definition.py +++ b/test/test_api/test_api_classes_follow_definition.py @@ -4,26 +4,26 @@ import jedi from ..helpers import cwd_at -def test_import_empty(): +def test_import_empty(Script): """ github #340, return the full word. """ - completion = jedi.Script("import ").completions()[0] + completion = Script("import ").completions()[0] definition = completion.follow_definition()[0] assert definition -def check_follow_definition_types(source): +def check_follow_definition_types(Script, source): # nested import - completions = jedi.Script(source, path='some_path.py').completions() + completions = Script(source, path='some_path.py').completions() defs = chain.from_iterable(c.follow_definition() for c in completions) return [d.type for d in defs] -def test_follow_import_incomplete(): +def test_follow_import_incomplete(Script): """ Completion on incomplete imports should always take the full completion to do any evaluation. """ - datetime = check_follow_definition_types("import itertool") + datetime = check_follow_definition_types(Script, "import itertool") assert datetime == ['module'] # empty `from * import` parts @@ -33,30 +33,30 @@ def test_follow_import_incomplete(): assert [d.type for d in definitions[0].follow_definition()] == ['class'] # incomplete `from * import` part - datetime = check_follow_definition_types("from datetime import datetim") + datetime = check_follow_definition_types(Script, "from datetime import datetim") assert set(datetime) == set(['class', 'instance']) # py33: builtin and pure py version # os.path check - ospath = check_follow_definition_types("from os.path import abspat") + ospath = check_follow_definition_types(Script, "from os.path import abspat") assert ospath == ['function'] # alias - alias = check_follow_definition_types("import io as abcd; abcd") + alias = check_follow_definition_types(Script, "import io as abcd; abcd") assert alias == ['module'] @cwd_at('test/completion/import_tree') -def test_follow_definition_nested_import(): - types = check_follow_definition_types("import pkg.mod1; pkg") +def test_follow_definition_nested_import(Script): + types = check_follow_definition_types(Script, "import pkg.mod1; pkg") assert types == ['module'] - types = check_follow_definition_types("import pkg.mod1; pkg.mod1") + types = check_follow_definition_types(Script, "import pkg.mod1; pkg.mod1") assert types == ['module'] - types = check_follow_definition_types("import pkg.mod1; pkg.mod1.a") + types = check_follow_definition_types(Script, "import pkg.mod1; pkg.mod1.a") assert types == ['instance'] -def test_follow_definition_land_on_import(): - types = check_follow_definition_types("import datetime; datetim") +def test_follow_definition_land_on_import(Script): + types = check_follow_definition_types(Script, "import datetime; datetim") assert types == ['module'] diff --git a/test/test_api/test_call_signatures.py b/test/test_api/test_call_signatures.py index ffafb4ea..77a58005 100644 --- a/test/test_api/test_call_signatures.py +++ b/test/test_api/test_call_signatures.py @@ -2,13 +2,14 @@ from textwrap import dedent import inspect import warnings +import pytest + from ..helpers import TestCase -from jedi import Script from jedi import cache -from jedi._compatibility import is_py33, py_version +from jedi._compatibility import is_py33 -def assert_signature(source, expected_name, expected_index=0, line=None, column=None): +def assert_signature(Script, source, expected_name, expected_index=0, line=None, column=None): signatures = Script(source, line, column).call_signatures() assert len(signatures) <= 1 @@ -22,12 +23,17 @@ def assert_signature(source, expected_name, expected_index=0, line=None, column= return signatures[0] -class TestCallSignatures(TestCase): - def _run_simple(self, source, name, index=0, column=None, line=1): - assert_signature(source, name, index, line, column) +def test_valid_call(Script): + assert_signature(Script, 'str()', 'str', column=4) - def test_valid_call(self): - assert_signature('str()', 'str', column=4) + +class TestCallSignatures(TestCase): + @pytest.fixture(autouse=True) + def init(self, Script): + self.Script = Script + + def _run_simple(self, source, name, index=0, column=None, line=1): + assert_signature(self.Script, source, name, index, line, column) def test_simple(self): run = self._run_simple @@ -89,170 +95,185 @@ class TestCallSignatures(TestCase): self._run_simple("for sorted(", 'sorted', 0) self._run_simple("for s in sorted(", 'sorted', 0) - def test_complex(self): - s = """ - def abc(a,b): - pass - def a(self): - abc( +def test_call_signatures_empty_parentheses_pre_space(Script): + s = dedent("""\ + def f(a, b): + pass + f( )""") + assert_signature(Script, s, 'f', 0, line=3, column=3) - if 1: - pass - """ - assert_signature(s, 'abc', 0, line=6, column=24) - s = """ - import re - def huhu(it): - re.compile( - return it * 2 - """ - assert_signature(s, 'compile', 0, line=4, column=31) - # jedi-vim #70 - s = """def foo(""" - assert Script(s).call_signatures() == [] - - # jedi-vim #116 - s = """import itertools; test = getattr(itertools, 'chain'); test(""" - assert_signature(s, 'chain', 0) - - def test_call_signature_on_module(self): - """github issue #240""" - s = 'import datetime; datetime(' - # just don't throw an exception (if numpy doesn't exist, just ignore it) - assert Script(s).call_signatures() == [] - - def test_call_signatures_empty_parentheses_pre_space(self): - s = dedent("""\ +def test_multiple_signatures(Script): + s = dedent("""\ + if x: def f(a, b): pass - f( )""") - assert_signature(s, 'f', 0, line=3, column=3) - - def test_multiple_signatures(self): - s = dedent("""\ - if x: - def f(a, b): - pass - else: - def f(a, b): - pass - f(""") - assert len(Script(s).call_signatures()) == 2 - - def test_call_signatures_whitespace(self): - s = dedent("""\ - abs( - def x(): + else: + def f(a, b): pass - """) - assert_signature(s, 'abs', 0, line=1, column=5) + f(""") + assert len(Script(s).call_signatures()) == 2 - def test_decorator_in_class(self): + +def test_call_signatures_whitespace(Script): + s = dedent("""\ + abs( + def x(): + pass + """) + assert_signature(Script, s, 'abs', 0, line=1, column=5) + + +def test_decorator_in_class(Script): + """ + There's still an implicit param, with a decorator. + Github issue #319. + """ + s = dedent("""\ + def static(func): + def wrapped(obj, *args): + return f(type(obj), *args) + return wrapped + + class C(object): + @static + def test(cls): + return 10 + + C().test(""") + + signatures = Script(s).call_signatures() + assert len(signatures) == 1 + x = [p.description for p in signatures[0].params] + assert x == ['param *args'] + + +def test_additional_brackets(Script): + assert_signature(Script, 'str((', 'str', 0) + + +def test_unterminated_strings(Script): + assert_signature(Script, 'str(";', 'str', 0) + + +def test_whitespace_before_bracket(Script): + assert_signature(Script, 'str (', 'str', 0) + assert_signature(Script, 'str (";', 'str', 0) + assert_signature(Script, 'str\n(', None) + + +def test_brackets_in_string_literals(Script): + assert_signature(Script, 'str (" (', 'str', 0) + assert_signature(Script, 'str (" )', 'str', 0) + + +def test_function_definitions_should_break(Script): + """ + Function definitions (and other tokens that cannot exist within call + signatures) should break and not be able to return a call signature. + """ + assert_signature(Script, 'str(\ndef x', 'str', 0) + assert not Script('str(\ndef x(): pass').call_signatures() + + +def test_flow_call(Script): + assert not Script('if (1').call_signatures() + + +def test_chained_calls(Script): + source = dedent(''' + class B(): + def test2(self, arg): + pass + + class A(): + def test1(self): + return B() + + A().test1().test2(''') + + assert_signature(Script, source, 'test2', 0) + + +def test_return(Script): + source = dedent(''' + def foo(): + return '.'.join()''') + + assert_signature(Script, source, 'join', 0, column=len(" return '.'.join(")) + + +def test_call_signature_on_module(Script): + """github issue #240""" + s = 'import datetime; datetime(' + # just don't throw an exception (if numpy doesn't exist, just ignore it) + assert Script(s).call_signatures() == [] + + +def test_complex(Script): + s = """ + def abc(a,b): + pass + + def a(self): + abc( + + if 1: + pass """ - There's still an implicit param, with a decorator. - Github issue #319. + assert_signature(Script, s, 'abc', 0, line=6, column=20) + s = """ + import re + def huhu(it): + re.compile( + return it * 2 """ - s = dedent("""\ - def static(func): - def wrapped(obj, *args): - return f(type(obj), *args) - return wrapped + assert_signature(Script, s, 'compile', 0, line=4, column=27) - class C(object): - @static - def test(cls): - return 10 + # jedi-vim #70 + s = """def foo(""" + assert Script(s).call_signatures() == [] - C().test(""") - - signatures = Script(s).call_signatures() - assert len(signatures) == 1 - x = [p.description for p in signatures[0].params] - assert x == ['param *args'] - - def test_additional_brackets(self): - assert_signature('str((', 'str', 0) - - def test_unterminated_strings(self): - assert_signature('str(";', 'str', 0) - - def test_whitespace_before_bracket(self): - assert_signature('str (', 'str', 0) - assert_signature('str (";', 'str', 0) - assert_signature('str\n(', None) - - def test_brackets_in_string_literals(self): - assert_signature('str (" (', 'str', 0) - assert_signature('str (" )', 'str', 0) - - def test_function_definitions_should_break(self): - """ - Function definitions (and other tokens that cannot exist within call - signatures) should break and not be able to return a call signature. - """ - assert_signature('str(\ndef x', 'str', 0) - assert not Script('str(\ndef x(): pass').call_signatures() - - def test_flow_call(self): - assert not Script('if (1').call_signatures() - - def test_chained_calls(self): - source = dedent(''' - class B(): - def test2(self, arg): - pass - - class A(): - def test1(self): - return B() - - A().test1().test2(''') - - assert_signature(source, 'test2', 0) - - def test_return(self): - source = dedent(''' - def foo(): - return '.'.join()''') - - assert_signature(source, 'join', 0, column=len(" return '.'.join(")) + # jedi-vim #116 + s = """import itertools; test = getattr(itertools, 'chain'); test(""" + assert_signature(Script, s, 'chain', 0) -class TestParams(TestCase): - def params(self, source, line=None, column=None): - signatures = Script(source, line, column).call_signatures() - assert len(signatures) == 1 - return signatures[0].params - - def test_param_name(self): - if not is_py33: - p = self.params('''int(''') - # int is defined as: `int(x[, base])` - assert p[0].name == 'x' - # `int` docstring has been redefined: - # http://bugs.python.org/issue14783 - # TODO have multiple call signatures for int (like in the docstr) - #assert p[1].name == 'base' - - p = self.params('''open(something,''') - assert p[0].name in ['file', 'name'] - assert p[1].name == 'mode' - - def test_builtins(self): - """ - The self keyword should be visible even for builtins, if not - instantiated. - """ - p = self.params('str.endswith(') - assert p[0].name == 'self' - assert p[1].name == 'suffix' - p = self.params('str().endswith(') - assert p[0].name == 'suffix' +def _params(Script, source, line=None, column=None): + signatures = Script(source, line, column).call_signatures() + assert len(signatures) == 1 + return signatures[0].params -def test_signature_is_definition(): +def test_param_name(Script): + if not is_py33: + p = _params(Script, '''int(''') + # int is defined as: `int(x[, base])` + assert p[0].name == 'x' + # `int` docstring has been redefined: + # http://bugs.python.org/issue14783 + # TODO have multiple call signatures for int (like in the docstr) + #assert p[1].name == 'base' + + p = _params(Script, '''open(something,''') + assert p[0].name in ['file', 'name'] + assert p[1].name == 'mode' + + +def test_builtins(Script): + """ + The self keyword should be visible even for builtins, if not + instantiated. + """ + p = _params(Script, 'str.endswith(') + assert p[0].name == 'self' + assert p[1].name == 'suffix' + p = _params(Script, 'str().endswith(') + assert p[0].name == 'suffix' + + +def test_signature_is_definition(Script): """ Through inheritance, a call signature is a sub class of Definition. Check if the attributes match. @@ -279,7 +300,7 @@ def test_signature_is_definition(): assert attribute == signature_attribute -def test_no_signature(): +def test_no_signature(Script): # str doesn't have a __call__ method assert Script('str()(').call_signatures() == [] @@ -292,7 +313,7 @@ def test_no_signature(): assert Script('').call_signatures() == [] -def test_dict_literal_in_incomplete_call(): +def test_dict_literal_in_incomplete_call(Script): source = """\ import json @@ -309,7 +330,7 @@ def test_dict_literal_in_incomplete_call(): assert script.call_signatures() -def test_completion_interference(): +def test_completion_interference(Script): """Seems to cause problems, see also #396.""" cache.parser_cache.pop(None, None) assert Script('open(').call_signatures() @@ -320,12 +341,12 @@ def test_completion_interference(): assert Script('open(').call_signatures() -def test_keyword_argument_index(): +def test_keyword_argument_index(Script, environment): def get(source, column=None): return Script(source, column=column).call_signatures()[0] # The signature of sorted changed from 2 to 3. - py2_offset = int(py_version < 30) + py2_offset = int(environment.version_info.major == 2) assert get('sorted([], key=a').index == 1 + py2_offset assert get('sorted([], key=').index == 1 + py2_offset assert get('sorted([], no_key=a').index is None @@ -354,7 +375,7 @@ def test_keyword_argument_index(): assert get(both + 'foo(a, b, c').index == 0 -def test_bracket_start(): +def test_bracket_start(Script): def bracket_start(src): signatures = Script(src).call_signatures() assert len(signatures) == 1 @@ -363,20 +384,20 @@ def test_bracket_start(): assert bracket_start('str(') == (1, 3) -def test_different_caller(): +def test_different_caller(Script): """ It's possible to not use names, but another function result or an array index and then get the call signature of it. """ - assert_signature('[str][0](', 'str', 0) - assert_signature('[str][0]()', 'str', 0, column=len('[str][0](')) + assert_signature(Script, '[str][0](', 'str', 0) + assert_signature(Script, '[str][0]()', 'str', 0, column=len('[str][0](')) - assert_signature('(str)(', 'str', 0) - assert_signature('(str)()', 'str', 0, column=len('(str)(')) + assert_signature(Script, '(str)(', 'str', 0) + assert_signature(Script, '(str)()', 'str', 0, column=len('(str)(')) -def test_in_function(): +def test_in_function(Script): code = dedent('''\ class X(): @property @@ -384,7 +405,7 @@ def test_in_function(): assert not Script(code).call_signatures() -def test_lambda_params(): +def test_lambda_params(Script): code = dedent('''\ my_lambda = lambda x: x+1 my_lambda(1)''') @@ -394,7 +415,7 @@ def test_lambda_params(): assert [p.name for p in sig.params] == ['x'] -def test_class_creation(): +def test_class_creation(Script): code = dedent('''\ class X(): def __init__(self, foo, bar): @@ -411,7 +432,7 @@ def test_class_creation(): assert [p.name for p in sig.params] == ['foo', 'bar'] -def test_call_magic_method(): +def test_call_magic_method(Script): code = dedent('''\ class X(): def __call__(self, baz): diff --git a/test/test_api/test_classes.py b/test/test_api/test_classes.py index b0f127c4..2089f833 100644 --- a/test/test_api/test_classes.py +++ b/test/test_api/test_classes.py @@ -6,19 +6,20 @@ from inspect import cleandoc import pytest -from jedi import Script, __doc__ as jedi_doc, names +import jedi +from jedi import __doc__ as jedi_doc, names from ..helpers import cwd_at from ..helpers import TestCase -def test_is_keyword(): +def test_is_keyword(Script): #results = Script('import ', 1, 1, None).goto_definitions() #assert len(results) == 1 and results[0].is_keyword is True results = Script('str', 1, 1, None).goto_definitions() assert len(results) == 1 and results[0].is_keyword is False -def test_basedefinition_type(): +def test_basedefinition_type(Script, environment): def make_definitions(): """ Return a list of definitions for parametrized tests. @@ -43,7 +44,7 @@ def test_basedefinition_type(): """) definitions = [] - definitions += names(source) + definitions += names(source, environment=environment) source += dedent(""" variable = sys or C or x or f or g or g() or h""") @@ -66,7 +67,7 @@ def test_basedefinition_type(): 'generator', 'statement', 'import', 'param') -def test_basedefinition_type_import(): +def test_basedefinition_type_import(Script): def get_types(source, **kwargs): return set([t.type for t in Script(source, **kwargs).completions()]) @@ -89,7 +90,7 @@ def test_basedefinition_type_import(): assert get_types('import json.tool', column=9) == set(['module']) -def test_function_call_signature_in_doc(): +def test_function_call_signature_in_doc(Script): defs = Script(""" def f(x, y=1, z='a'): pass @@ -98,7 +99,7 @@ def test_function_call_signature_in_doc(): assert "f(x, y=1, z='a')" in str(doc) -def test_class_call_signature(): +def test_class_call_signature(Script): defs = Script(""" class Foo: def __init__(self, x, y=1, z='a'): @@ -108,14 +109,14 @@ def test_class_call_signature(): assert "Foo(self, x, y=1, z='a')" in str(doc) -def test_position_none_if_builtin(): +def test_position_none_if_builtin(Script): gotos = Script('import sys; sys.path').goto_assignments() assert gotos[0].line is None assert gotos[0].column is None @cwd_at('.') -def test_completion_docstring(): +def test_completion_docstring(Script): """ Jedi should follow imports in certain conditions """ @@ -126,7 +127,7 @@ def test_completion_docstring(): c = Script('import jedi\njed').completions()[0] assert c.docstring(fast=False) == cleandoc(jedi_doc) - docstr('import jedi\njedi.Scr', cleandoc(Script.__doc__)) + docstr('import jedi\njedi.Scr', cleandoc(jedi.Script.__doc__)) docstr('abcd=3;abcd', '') docstr('"hello"\nabcd=3\nabcd', '') @@ -160,12 +161,12 @@ def test_completion_docstring(): ) -def test_completion_params(): +def test_completion_params(Script): c = Script('import string; string.capwords').completions()[0] assert [p.name for p in c.params] == ['s', 'sep'] -def test_signature_params(): +def test_signature_params(Script): def check(defs): params = defs[0].params assert len(params) == 1 @@ -182,7 +183,7 @@ def test_signature_params(): check(Script(s + '\nbar=foo\nbar').goto_assignments()) -def test_param_endings(): +def test_param_endings(Script): """ Params should be represented without the comma and whitespace they have around them. @@ -192,8 +193,17 @@ def test_param_endings(): class TestIsDefinition(TestCase): + @pytest.fixture(autouse=True) + def init(self, environment): + self.environment = environment + def _def(self, source, index=-1): - return names(dedent(source), references=True, all_scopes=True)[index] + return names( + dedent(source), + references=True, + all_scopes=True, + environment=self.environment + )[index] def _bool_is_definitions(self, source): ns = names(dedent(source), references=True, all_scopes=True) @@ -225,8 +235,12 @@ class TestIsDefinition(TestCase): class TestParent(TestCase): + @pytest.fixture(autouse=True) + def init(self, Script): + self.Script = Script + def _parent(self, source, line=None, column=None): - def_, = Script(dedent(source), line, column).goto_assignments() + def_, = self.Script(dedent(source), line, column).goto_assignments() return def_.parent() def test_parent(self): @@ -248,20 +262,21 @@ class TestParent(TestCase): assert parent.name == '' assert parent.type == 'module' - def test_parent_on_completion(self): - parent = Script(dedent('''\ - class Foo(): - def bar(): pass - Foo().bar''')).completions()[0].parent() - assert parent.name == 'Foo' - assert parent.type == 'class' - parent = Script('str.join').completions()[0].parent() - assert parent.name == 'str' - assert parent.type == 'class' +def test_parent_on_completion(Script): + parent = Script(dedent('''\ + class Foo(): + def bar(): pass + Foo().bar''')).completions()[0].parent() + assert parent.name == 'Foo' + assert parent.type == 'class' + + parent = Script('str.join').completions()[0].parent() + assert parent.name == 'str' + assert parent.type == 'class' -def test_type(): +def test_type(Script): for c in Script('a = [str()]; a[0].').completions(): if c.name == '__class__': assert c.type == 'class' @@ -272,7 +287,8 @@ def test_type(): for c in Script('import os; os.path.').completions(): assert c.type -def test_type_II(): + +def test_type_II(Script): """ GitHub Issue #833, `keyword`s are seen as `module`s """ @@ -281,100 +297,106 @@ def test_type_II(): assert c.type == 'keyword' -class TestGotoAssignments(TestCase): - """ - This tests the BaseDefinition.goto_assignments function, not the jedi - function. They are not really different in functionality, but really - different as an implementation. - """ - def test_repetition(self): - defs = names('a = 1; a', references=True, definitions=False) - # Repeat on the same variable. Shouldn't change once we're on a - # definition. - for _ in range(3): - assert len(defs) == 1 - ass = defs[0].goto_assignments() - assert ass[0].description == 'a = 1' - - def test_named_params(self): - src = """\ - def foo(a=1, bar=2): - pass - foo(bar=1) - """ - bar = names(dedent(src), references=True)[-1] - param = bar.goto_assignments()[0] - assert (param.line, param.column) == (1, 13) - assert param.type == 'param' - - def test_class_call(self): - src = 'from threading import Thread; Thread(group=1)' - n = names(src, references=True)[-1] - assert n.name == 'group' - param_def = n.goto_assignments()[0] - assert param_def.name == 'group' - assert param_def.type == 'param' - - def test_parentheses(self): - n = names('("").upper', references=True)[-1] - assert n.goto_assignments()[0].name == 'upper' - - def test_import(self): - nms = names('from json import load', references=True) - assert nms[0].name == 'json' - 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 == '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 == '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 == 'module' - - nms = names('import os.path', references=True) - n = nms[0].goto_assignments()[0] - assert n.name == 'os' - assert n.type == 'module' - n = nms[1].goto_assignments()[0] - # This is very special, normally the name doesn't chance, but since - # os.path is a sys.modules hack, it does. - assert n.name in ('ntpath', 'posixpath', 'os2emxpath') - assert n.type == 'module' - - def test_import_alias(self): - nms = names('import json as foo', references=True) - assert nms[0].name == 'json' - assert nms[0].type == 'module' - assert nms[0]._name.tree_name.parent.type == 'dotted_as_name' - n = nms[0].goto_assignments()[0] - assert n.name == 'json' - assert n.type == 'module' - assert n._name._context.tree_node.type == 'file_input' - - assert nms[1].name == 'foo' - assert nms[1].type == 'module' - assert nms[1]._name.tree_name.parent.type == 'dotted_as_name' - ass = nms[1].goto_assignments() - assert len(ass) == 1 - assert ass[0].name == 'json' - assert ass[0].type == 'module' - assert ass[0]._name._context.tree_node.type == 'file_input' +""" +This tests the BaseDefinition.goto_assignments function, not the jedi +function. They are not really different in functionality, but really +different as an implementation. +""" -def test_added_equals_to_params(): +def test_goto_assignment_repetition(environment): + defs = names('a = 1; a', references=True, definitions=False, environment=environment) + # Repeat on the same variable. Shouldn't change once we're on a + # definition. + for _ in range(3): + assert len(defs) == 1 + ass = defs[0].goto_assignments() + assert ass[0].description == 'a = 1' + + +def test_goto_assignments_named_params(environment): + src = """\ + def foo(a=1, bar=2): + pass + foo(bar=1) + """ + bar = names(dedent(src), references=True, environment=environment)[-1] + param = bar.goto_assignments()[0] + assert (param.line, param.column) == (1, 13) + assert param.type == 'param' + + +def test_class_call(environment): + src = 'from threading import Thread; Thread(group=1)' + n = names(src, references=True, environment=environment)[-1] + assert n.name == 'group' + param_def = n.goto_assignments()[0] + assert param_def.name == 'group' + assert param_def.type == 'param' + + +def test_parentheses(environment): + n = names('("").upper', references=True, environment=environment)[-1] + assert n.goto_assignments()[0].name == 'upper' + + +def test_import(environment): + nms = names('from json import load', references=True, environment=environment) + assert nms[0].name == 'json' + 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 == 'function' + n = nms[1].goto_assignments()[0] + assert n.name == 'load' + assert n.type == 'function' + + nms = names('import os; os.path', references=True, environment=environment) + assert nms[0].name == 'os' + 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 == 'module' + + nms = names('import os.path', references=True, environment=environment) + n = nms[0].goto_assignments()[0] + assert n.name == 'os' + assert n.type == 'module' + n = nms[1].goto_assignments()[0] + # This is very special, normally the name doesn't chance, but since + # os.path is a sys.modules hack, it does. + assert n.name in ('ntpath', 'posixpath', 'os2emxpath') + assert n.type == 'module' + + +def test_import_alias(environment): + nms = names('import json as foo', references=True, environment=environment) + assert nms[0].name == 'json' + assert nms[0].type == 'module' + assert nms[0]._name.tree_name.parent.type == 'dotted_as_name' + n = nms[0].goto_assignments()[0] + assert n.name == 'json' + assert n.type == 'module' + assert n._name._context.tree_node.type == 'file_input' + + assert nms[1].name == 'foo' + assert nms[1].type == 'module' + assert nms[1]._name.tree_name.parent.type == 'dotted_as_name' + ass = nms[1].goto_assignments() + assert len(ass) == 1 + assert ass[0].name == 'json' + assert ass[0].type == 'module' + assert ass[0]._name._context.tree_node.type == 'file_input' + + +def test_added_equals_to_params(Script): def run(rest_source): source = dedent(""" def foo(bar, baz): diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index 6cd7e04b..e9bbdb5b 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -1,15 +1,14 @@ from textwrap import dedent -from jedi import Script -def test_in_whitespace(): +def test_in_whitespace(Script): code = dedent(''' def x(): pass''') assert len(Script(code, column=2).completions()) > 20 -def test_empty_init(): +def test_empty_init(Script): """This was actually an issue.""" code = dedent('''\ class X(object): pass @@ -17,7 +16,7 @@ def test_empty_init(): assert Script(code).completions() -def test_in_empty_space(): +def test_in_empty_space(Script): code = dedent('''\ class X(object): def __init__(self): @@ -30,7 +29,7 @@ def test_in_empty_space(): assert def_.name == 'X' -def test_indent_context(): +def test_indent_context(Script): """ If an INDENT is the next supposed token, we should still be able to complete. @@ -40,7 +39,7 @@ def test_indent_context(): assert comp.name == 'isinstance' -def test_keyword_context(): +def test_keyword_context(Script): def get_names(*args, **kwargs): return [d.name for d in Script(*args, **kwargs).completions()] diff --git a/test/test_api/test_defined_names.py b/test/test_api/test_defined_names.py index b982594b..81b7c5b6 100644 --- a/test/test_api/test_defined_names.py +++ b/test/test_api/test_defined_names.py @@ -4,16 +4,22 @@ Tests for `api.defined_names`. from textwrap import dedent +import pytest + from jedi import names from ..helpers import TestCase class TestDefinedNames(TestCase): + @pytest.fixture(autouse=True) + def init(self, environment): + self.environment = environment + def assert_definition_names(self, definitions, names_): assert [d.name for d in definitions] == names_ def check_defined_names(self, source, names_): - definitions = names(dedent(source)) + definitions = names(dedent(source), environment=self.environment) self.assert_definition_names(definitions, names_) return definitions @@ -74,19 +80,19 @@ class TestDefinedNames(TestCase): self.assert_definition_names(subsubdefs[0].defined_names(), ['f']) -def test_follow_imports(): +def test_follow_imports(environment): # github issue #344 - imp = names('import datetime')[0] + imp = names('import datetime', environment=environment)[0] assert imp.name == 'datetime' datetime_names = [str(d.name) for d in imp.defined_names()] assert 'timedelta' in datetime_names -def test_names_twice(): +def test_names_twice(environment): source = dedent(''' def lol(): pass ''') - defs = names(source=source) + defs = names(source=source, environment=environment) assert defs[0].defined_names() == [] diff --git a/test/test_api/test_full_name.py b/test/test_api/test_full_name.py index d2ad9d02..eabe6ca2 100644 --- a/test/test_api/test_full_name.py +++ b/test/test_api/test_full_name.py @@ -24,8 +24,12 @@ from ..helpers import TestCase class MixinTestFullName(object): operation = None + @pytest.fixture(autouse=True) + def init(self, Script): + self.Script = Script + def check(self, source, desired): - script = jedi.Script(textwrap.dedent(source)) + script = self.Script(textwrap.dedent(source)) definitions = getattr(script, type(self).operation)() for d in definitions: self.assertEqual(d.full_name, desired) @@ -80,25 +84,25 @@ class TestFullDefinedName(TestCase): """, ['os', 'os.path', 'os.path.join', 'os.path']) -def test_sub_module(): +def test_sub_module(Script): """ ``full_name needs to check sys.path to actually find it's real path module path. """ - defs = jedi.Script('from jedi.api import classes; classes').goto_definitions() + defs = Script('from jedi.api import classes; classes').goto_definitions() assert [d.full_name for d in defs] == ['jedi.api.classes'] - defs = jedi.Script('import jedi.api; jedi.api').goto_definitions() + defs = Script('import jedi.api; jedi.api').goto_definitions() assert [d.full_name for d in defs] == ['jedi.api'] -def test_os_path(): - d, = jedi.Script('from os.path import join').completions() +def test_os_path(Script): + d, = Script('from os.path import join').completions() assert d.full_name == 'os.path.join' - d, = jedi.Script('import os.p').completions() + d, = Script('import os.p').completions() assert d.full_name == 'os.path' -def test_os_issues(): +def test_os_issues(Script): """Issue #873""" - c, = jedi.Script('import os\nos.nt''').completions() + c, = Script('import os\nos.nt''').completions() assert c.full_name == 'nt' diff --git a/test/test_api/test_unicode.py b/test/test_api/test_unicode.py index bcd8ad42..4d3fdf5c 100644 --- a/test/test_api/test_unicode.py +++ b/test/test_api/test_unicode.py @@ -2,11 +2,10 @@ """ All character set and unicode related tests. """ -from jedi import Script from jedi._compatibility import u, unicode -def test_unicode_script(): +def test_unicode_script(Script): """ normally no unicode objects are being used. (<=2.7) """ s = unicode("import datetime; datetime.timedelta") completions = Script(s).completions() @@ -24,7 +23,7 @@ def test_unicode_script(): assert type(completions[0].description) is unicode -def test_unicode_attribute(): +def test_unicode_attribute(Script): """ github jedi-vim issue #94 """ s1 = u('#-*- coding: utf-8 -*-\nclass Person():\n' ' name = "e"\n\nPerson().name.') @@ -36,7 +35,7 @@ def test_unicode_attribute(): assert 'strip' in [c.name for c in completions2] -def test_multibyte_script(): +def test_multibyte_script(Script): """ `jedi.Script` must accept multi-byte string source. """ try: code = u("import datetime; datetime.d") @@ -48,7 +47,7 @@ def test_multibyte_script(): assert len(Script(s, 1, len(code)).completions()) -def test_goto_definition_at_zero(): +def test_goto_definition_at_zero(Script): """At zero usually sometimes raises unicode issues.""" assert Script("a", 1, 1).goto_definitions() == [] s = Script("str", 1, 1).goto_definitions() @@ -57,7 +56,7 @@ def test_goto_definition_at_zero(): assert Script("", 1, 0).goto_definitions() == [] -def test_complete_at_zero(): +def test_complete_at_zero(Script): s = Script("str", 1, 3).completions() assert len(s) == 1 assert list(s)[0].name == 'str' diff --git a/test/test_api/test_usages.py b/test/test_api/test_usages.py index 1325e412..94deb0f9 100644 --- a/test/test_api/test_usages.py +++ b/test/test_api/test_usages.py @@ -1,6 +1,3 @@ -import jedi - - -def test_import_usage(): - s = jedi.Script("from .. import foo", line=1, column=18, path="foo.py") +def test_import_usage(Script): + s = Script("from .. import foo", line=1, column=18, path="foo.py") assert [usage.line for usage in s.usages()] == [1]