From c801e24afcd0b7e7d6637200baf91f371993b40c Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Tue, 12 Feb 2019 14:14:11 +0100 Subject: [PATCH 1/4] fix get_system_environment misses if same python version has multiple installs The Environment.__init__ may throw an InvalidPythonEnvironment if the call to _get_subprocess() fails. In this case other registered Python environments of the same Python version may still work and shall also be considered. --- jedi/api/environment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jedi/api/environment.py b/jedi/api/environment.py index 9fbdeacf..d5f98c3d 100644 --- a/jedi/api/environment.py +++ b/jedi/api/environment.py @@ -333,7 +333,10 @@ def get_system_environment(version): if os.name == 'nt': for exe in _get_executables_from_windows_registry(version): - return Environment(exe) + try: + return Environment(exe) + except InvalidPythonEnvironment: + pass raise InvalidPythonEnvironment("Cannot find executable python%s." % version) From 1e12e1e318ada17b27cf726734e257339d6d649a Mon Sep 17 00:00:00 2001 From: forest93 Date: Thu, 11 Apr 2019 23:38:55 +0800 Subject: [PATCH 2/4] Make get_module_names return top-level async functions when all_scopes=False. --- jedi/evaluate/helpers.py | 3 +++ test/test_api/test_defined_names.py | 24 ++++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 1b0a7f2a..c087e2fb 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -190,6 +190,9 @@ def get_module_names(module, all_scopes): # but that would be a big change that could break type inference, whereas for now # this discrepancy looks like only a problem for "get_module_names". parent_scope = parent_scope.parent + # async functions have an extra wrapper. Strip it. + if parent_scope and parent_scope.type == 'async_stmt': + parent_scope = parent_scope.parent return parent_scope in (module, None) names = [n for n in names if is_module_scope_name(n)] diff --git a/test/test_api/test_defined_names.py b/test/test_api/test_defined_names.py index 31814e0f..5c8bf05f 100644 --- a/test/test_api/test_defined_names.py +++ b/test/test_api/test_defined_names.py @@ -82,18 +82,34 @@ class TestDefinedNames(TestCase): def test_class_fields_with_all_scopes_false(self): definitions = self.check_defined_names(""" from module import f + import asyncio + g = f(f) class C: h = g + def __init__(self): + pass + + async def __aenter__(self): + pass def foo(x=a): bar = x return bar - """, ['f', 'g', 'C', 'foo']) - C_subdefs = definitions[-2].defined_names() - foo_subdefs = definitions[-1].defined_names() - self.assert_definition_names(C_subdefs, ['h']) + + async def async_foo(duration): + async def wait(): + await asyncio.sleep(100) + for i in range(duration//100): + await wait() + return duration//100*100 + """, ['f', 'asyncio', 'g', 'C', 'foo', 'async_foo']) + C_subdefs = definitions[-3].defined_names() + foo_subdefs = definitions[-2].defined_names() + async_foo_subdefs = definitions[-1].defined_names() + self.assert_definition_names(C_subdefs, ['h', '__init__', '__aenter__']) self.assert_definition_names(foo_subdefs, ['x', 'bar']) + self.assert_definition_names(async_foo_subdefs, ['duration', 'wait', 'i']) def test_follow_imports(environment): From 2724ac9e07db873fda208f3586b6c228f557d7e9 Mon Sep 17 00:00:00 2001 From: forest93 Date: Fri, 12 Apr 2019 23:31:06 +0800 Subject: [PATCH 3/4] Make a separate test case. --- test/test_api/test_defined_names.py | 31 ++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/test/test_api/test_defined_names.py b/test/test_api/test_defined_names.py index 5c8bf05f..b70807fe 100644 --- a/test/test_api/test_defined_names.py +++ b/test/test_api/test_defined_names.py @@ -80,6 +80,22 @@ class TestDefinedNames(TestCase): self.assert_definition_names(subsubdefs[0].defined_names(), ['f']) def test_class_fields_with_all_scopes_false(self): + definitions = self.check_defined_names(""" + from module import f + g = f(f) + class C: + h = g + + def foo(x=a): + bar = x + return bar + """, ['f', 'g', 'C', 'foo']) + C_subdefs = definitions[-2].defined_names() + foo_subdefs = definitions[-1].defined_names() + self.assert_definition_names(C_subdefs, ['h']) + self.assert_definition_names(foo_subdefs, ['x', 'bar']) + + def test_async_stmt_with_all_scopes_false(self): definitions = self.check_defined_names(""" from module import f import asyncio @@ -103,14 +119,19 @@ class TestDefinedNames(TestCase): for i in range(duration//100): await wait() return duration//100*100 - """, ['f', 'asyncio', 'g', 'C', 'foo', 'async_foo']) - C_subdefs = definitions[-3].defined_names() - foo_subdefs = definitions[-2].defined_names() - async_foo_subdefs = definitions[-1].defined_names() + + async with C() as cinst: + d = cinst + """, ['f', 'asyncio', 'g', 'C', 'foo', 'async_foo', 'cinst', 'd']) + C_subdefs = definitions[3].defined_names() + foo_subdefs = definitions[4].defined_names() + async_foo_subdefs = definitions[5].defined_names() + cinst_subdefs = definitions[6].defined_names() self.assert_definition_names(C_subdefs, ['h', '__init__', '__aenter__']) self.assert_definition_names(foo_subdefs, ['x', 'bar']) self.assert_definition_names(async_foo_subdefs, ['duration', 'wait', 'i']) - + # We treat d as a name outside `async with` block + self.assert_definition_names(cinst_subdefs, []) def test_follow_imports(environment): # github issue #344 From e843c6108d761179fef4154a400e5cd3ab10d832 Mon Sep 17 00:00:00 2001 From: Andreas Mittelberger Date: Fri, 12 Apr 2019 15:41:02 -0700 Subject: [PATCH 4/4] fix add_bracket_after_function had no effect (#1297) * fix add_bracket_after_function had no effect * added test for fix * using monkeypatch to set add_bracket_after_function. --- jedi/api/classes.py | 2 +- test/test_api/test_settings.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/jedi/api/classes.py b/jedi/api/classes.py index 9602e04a..37513e86 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -397,7 +397,7 @@ class Completion(BaseDefinition): def _complete(self, like_name): append = '' if settings.add_bracket_after_function \ - and self.type == 'Function': + and self.type == 'function': append = '(' if self._name.api_type == 'param' and self._stack is not None: diff --git a/test/test_api/test_settings.py b/test/test_api/test_settings.py index 522bf82d..24ae05fe 100644 --- a/test/test_api/test_settings.py +++ b/test/test_api/test_settings.py @@ -21,3 +21,14 @@ def test_add_dynamic_mods(Script): result = script.goto_definitions() assert len(result) == 1 assert result[0].description == 'class int' + + +def test_add_bracket_after_function(monkeypatch, Script): + settings = api.settings + monkeypatch.setattr(settings, 'add_bracket_after_function', True) + script = Script('''\ +def foo(): + pass +foo''') + completions = script.completions() + assert completions[0].complete == '('