Auto merge of #1896 - micbou:path-to-python-interpreter, r=Valloric
[READY] Implement new strategy to find the Python interpreter path See discussion in issue #1891 for details. Implement a new strategy to find the Python interpreter path: - if specified, use `g:ycm_path_to_python_interpreter` option. - on UNIX platforms, use `sys.executable` as the path to Python interpreter; - on Windows, deduce it from `os.__file__` path (it should always be in the parent folder). In all cases, check the version (2.6 or 2.7) of the Python interpreter path by running it. This PR may break things. It needs thorough testing. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/valloric/youcompleteme/1896) <!-- Reviewable:end -->
This commit is contained in:
commit
8d5f164feb
@ -18,14 +18,16 @@
|
||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import vim
|
||||
import functools
|
||||
import re
|
||||
from ycmd import utils
|
||||
|
||||
DIR_OF_CURRENT_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) )
|
||||
|
||||
WIN_PYTHON27_PATH = 'C:\python27\python.exe'
|
||||
WIN_PYTHON26_PATH = 'C:\python26\python.exe'
|
||||
WIN_PYTHON_PATH = os.path.join( sys.exec_prefix, 'python.exe' )
|
||||
PYTHON_BINARY_REGEX = re.compile( r'python(2(\.[67])?)?(.exe)?$' )
|
||||
|
||||
|
||||
def Memoize( obj ):
|
||||
@ -42,29 +44,56 @@ def Memoize( obj ):
|
||||
|
||||
@Memoize
|
||||
def PathToPythonInterpreter():
|
||||
user_path_to_python = vim.eval( 'g:ycm_path_to_python_interpreter' )
|
||||
python_interpreter = vim.eval( 'g:ycm_path_to_python_interpreter' )
|
||||
|
||||
if user_path_to_python:
|
||||
return user_path_to_python
|
||||
if python_interpreter:
|
||||
if IsPythonVersionCorrect( python_interpreter ):
|
||||
return python_interpreter
|
||||
|
||||
# We check for 'python2' before 'python' because some OS's (I'm looking at
|
||||
# you Arch Linux) have made the... interesting decision to point
|
||||
# /usr/bin/python to python3.
|
||||
python_names = [ 'python2', 'python' ]
|
||||
raise RuntimeError( "Path in 'g:ycm_path_to_python_interpreter' option "
|
||||
"does not point to a valid Python 2.6 or 2.7." )
|
||||
|
||||
path_to_python = utils.PathToFirstExistingExecutable( python_names )
|
||||
if path_to_python:
|
||||
return path_to_python
|
||||
# On UNIX platforms, we use sys.executable as the Python interpreter path.
|
||||
# We cannot use sys.executable on Windows because for unknown reasons, it
|
||||
# returns the Vim executable. Instead, we use sys.exec_prefix to deduce the
|
||||
# interpreter path.
|
||||
python_interpreter = ( WIN_PYTHON_PATH if utils.OnWindows() else
|
||||
sys.executable )
|
||||
|
||||
# On Windows, Python may not be on the PATH at all, so we check some common
|
||||
# install locations.
|
||||
if utils.OnWindows():
|
||||
if os.path.exists( WIN_PYTHON27_PATH ):
|
||||
return WIN_PYTHON27_PATH
|
||||
elif os.path.exists( WIN_PYTHON26_PATH ):
|
||||
return WIN_PYTHON26_PATH
|
||||
if IsPythonVersionCorrect( python_interpreter ):
|
||||
return python_interpreter
|
||||
|
||||
raise RuntimeError( 'Python 2.7/2.6 not installed!' )
|
||||
# As a last resort, we search python in the PATH. We check 'python2' before
|
||||
# 'python' because on some distributions (Arch Linux for example), python
|
||||
# refers to python3.
|
||||
python_interpreter = utils.PathToFirstExistingExecutable( [ 'python2',
|
||||
'python' ] )
|
||||
|
||||
if IsPythonVersionCorrect( python_interpreter ):
|
||||
return python_interpreter
|
||||
|
||||
raise RuntimeError( "Cannot find Python 2.6 or 2.7. You can set its path "
|
||||
"using the 'g:ycm_path_to_python_interpreter' "
|
||||
"option." )
|
||||
|
||||
|
||||
def EndsWithPython( path ):
|
||||
"""Check if given path ends with a python 2.6 or 2.7 name."""
|
||||
return PYTHON_BINARY_REGEX.search( path ) is not None
|
||||
|
||||
|
||||
def IsPythonVersionCorrect( path ):
|
||||
"""Check if given path is the Python interpreter version 2.6 or 2.7."""
|
||||
if not EndsWithPython( path ):
|
||||
return False
|
||||
|
||||
command = [ path,
|
||||
'-c',
|
||||
"import sys;"
|
||||
"major, minor = sys.version_info[ :2 ];"
|
||||
"sys.exit( major != 2 or minor < 6)" ]
|
||||
|
||||
return utils.SafePopen( command ).wait() == 0
|
||||
|
||||
|
||||
def PathToServerScript():
|
||||
|
47
python/ycm/tests/paths_test.py
Normal file
47
python/ycm/tests/paths_test.py
Normal file
@ -0,0 +1,47 @@
|
||||
#
|
||||
# Copyright (C) 2016 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
# YouCompleteMe is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# YouCompleteMe is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ycm.test_utils import MockVimModule
|
||||
MockVimModule()
|
||||
|
||||
from nose.tools import ok_
|
||||
from ycm.paths import EndsWithPython
|
||||
|
||||
|
||||
def EndsWithPython_Python2Paths_test():
|
||||
python_paths = [
|
||||
'python',
|
||||
'/usr/bin/python2.6',
|
||||
'/home/user/.pyenv/shims/python2.7',
|
||||
r'C:\Python26\python.exe'
|
||||
]
|
||||
|
||||
for path in python_paths:
|
||||
ok_( EndsWithPython( path ) )
|
||||
|
||||
|
||||
def EndsWithPython_NotPython2Paths_test():
|
||||
not_python_paths = [
|
||||
'/opt/local/bin/vim',
|
||||
r'C:\Program Files\Vim\vim74\gvim.exe',
|
||||
'/usr/bin/python3',
|
||||
'/home/user/.pyenv/shims/python3',
|
||||
]
|
||||
|
||||
for path in not_python_paths:
|
||||
ok_( not EndsWithPython( path ) )
|
Loading…
Reference in New Issue
Block a user