From b4a4dacebdcec303d68155c33f9cdf8746a5809d Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Thu, 21 Feb 2019 10:18:40 +0100 Subject: [PATCH] Fix embedded Python with Jedi (see comments in source code), fixes davidhalter/jedi-vim#870 --- jedi/api/environment.py | 34 +++++++++++++++++++++++++++++-- test/test_api/test_environment.py | 9 ++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/jedi/api/environment.py b/jedi/api/environment.py index ea34d90b..5dac55c2 100644 --- a/jedi/api/environment.py +++ b/jedi/api/environment.py @@ -184,9 +184,39 @@ def get_default_environment(): if virtual_env is not None: return virtual_env - # If no VirtualEnv is found, use the environment we're already + env = SameEnvironment() + if not os.path.basename(env.executable).lower().startswith('python'): + # This tries to counter issues with embedding. In some cases (e.g. + # VIM's Python Mac/Windows, sys.executable is /foo/bar/vim. This + # happens, because for Mac a function called `_NSGetExecutablePath` is + # used and for Windows `GetModuleFileNameW`. These are both platform + # specific functions. For all other systems sys.executable should be + # alright. However here we try to generalize: + # + # 1. Check if the executable looks like python (heuristic) + # 2. In case it's not try to find the executable + # 3. In case we don't find it use an interpreter environment. + # + # The last option will always work, but leads to potential crashes of + # Jedi - which is ok, because it happens very rarely and even less, + # because the code below should work for most cases. + if os.name == 'nt': + # The first case would be a virtualenv and the second a normal + # Python installation. + checks = (r'Scripts\python.exe', 'python.exe') + else: + # For unix it looks like Python is always in a bin folder. + checks = ('bin/python',) + for check in checks: + guess = os.path.join(sys.exec_prefix, check) + if os.path.isfile(guess): + # Bingo - We think we have our Python. + return Environment(guess) + # It looks like there is no reasonable Python to be found. + return InterpreterEnvironment() + # If no virtualenv is found, use the environment we're already # using. - return SameEnvironment() + return env def get_cached_default_environment(): diff --git a/test/test_api/test_environment.py b/test/test_api/test_environment.py index 6e2cf36b..834f0ebd 100644 --- a/test/test_api/test_environment.py +++ b/test/test_api/test_environment.py @@ -131,6 +131,15 @@ def test_get_default_environment_from_env_does_not_use_safe(tmpdir, monkeypatch) assert env.path == 'fake' +def test_get_default_environment_when_embedded(monkeypatch): + # When using Python embedded, sometimes the executable is not a Python + # executable. + executable_name = 'RANDOM_EXE' + monkeypatch.setattr(sys, 'executable', executable_name) + env = get_default_environment() + assert env.executable != executable_name + + def test_changing_venv(venv_path, monkeypatch): monkeypatch.setitem(os.environ, 'VIRTUAL_ENV', venv_path) get_cached_default_environment()