diff --git a/spyder/plugins/maininterpreter.py b/spyder/plugins/maininterpreter.py index 50415fb3d1c..ef47ce9180c 100644 --- a/spyder/plugins/maininterpreter.py +++ b/spyder/plugins/maininterpreter.py @@ -141,7 +141,15 @@ def python_executable_changed(self, pyexec): return if not is_text_string(pyexec): pyexec = to_text_string(pyexec.toUtf8(), 'utf-8') - self.warn_python_compatibility(pyexec) + if programs.is_python_interpreter(pyexec): + self.warn_python_compatibility(pyexec) + else: + QMessageBox.warning(self, _('Warning'), + _("You selected an invalid Python interpreter for the " + "console so the previous interpreter will stay. Please " + "make sure to select a valid one."), QMessageBox.Ok) + self.pyexec_edit.setText(get_python_executable()) + return def python_executable_switched(self, custom): """Python executable default/custom radio button has been toggled""" diff --git a/spyder/utils/programs.py b/spyder/utils/programs.py index 41e3dd39ebd..783124c47f9 100644 --- a/spyder/utils/programs.py +++ b/spyder/utils/programs.py @@ -20,7 +20,7 @@ # Local imports from spyder.utils import encoding -from spyder.py3compat import PY2, is_text_string +from spyder.py3compat import PY2, is_text_string, to_text_string class ProgramError(Exception): @@ -462,6 +462,32 @@ def is_module_installed(module_name, version=None, installed_version=None, return check_version(actver, version, symb) +def is_python_interpreter_valid_name(filename): + """Check that the python interpreter file has a valid name.""" + pattern = r'.*python(\d\.?\d*)?(w)?(.exe)?$' + if re.match(pattern, filename, flags=re.I) is None: + return False + else: + return True + +def is_python_interpreter(filename): + """Evaluate wether a file is a python interpreter or not.""" + real_filename = os.path.realpath(filename) # To follow symlink if existent + if (not osp.isfile(real_filename) or encoding.is_text_file(real_filename) + or not is_python_interpreter_valid_name(filename)): + return False + try: + proc = run_program(filename, ["-h"]) + output = to_text_string(proc.communicate()[0]) + valid = ("Options and arguments (and corresponding environment " + "variables)") + if 'usage:' in output and valid in output: + return True + else: + return False + except: + return False + def test_programs(): assert find_program('git') diff --git a/spyder/utils/tests/test_programs.py b/spyder/utils/tests/test_programs.py index 3db8b8f2096..b45bfd9ba6d 100644 --- a/spyder/utils/tests/test_programs.py +++ b/spyder/utils/tests/test_programs.py @@ -6,10 +6,22 @@ """Tests for programs.py""" import os - import pytest -from spyder.utils.programs import run_python_script_in_terminal +from spyder.utils.programs import (run_python_script_in_terminal, + is_python_interpreter, + is_python_interpreter_valid_name) + + +if os.name == 'nt': + python_dir = os.environ['PYTHON'] + VALID_INTERPRETER = os.path.join(python_dir, 'python.exe') + INVALID_INTERPRETER = os.path.join(python_dir, 'Scripts', 'ipython.exe') +else: + home_dir = os.environ['HOME'] + VALID_INTERPRETER = os.path.join(home_dir, 'miniconda', 'bin', 'python') + INVALID_INTERPRETER = os.path.join(home_dir, 'miniconda', 'bin', 'ipython') + @pytest.mark.skipif(os.name == 'nt', reason='gets stuck on Windows') # FIXME def test_run_python_script_in_terminal(tmpdir, qtbot): @@ -24,6 +36,7 @@ def test_run_python_script_in_terminal(tmpdir, qtbot): res = outfilepath.read() assert res == 'done' + @pytest.mark.skipif(os.name == 'nt', reason='gets stuck on Windows') # FIXME def test_run_python_script_in_terminal_with_wdir_empty(tmpdir, qtbot): scriptpath = tmpdir.join('write-done.py') @@ -36,7 +49,24 @@ def test_run_python_script_in_terminal_with_wdir_empty(tmpdir, qtbot): res = outfilepath.read() assert res == 'done' - + +@pytest.mark.skipif(os.environ.get('CI', None) == True, + reason='It only runs in CI services.') +def test_is_valid_interpreter(): + assert is_python_interpreter(VALID_INTERPRETER) + + +@pytest.mark.skipif(os.environ.get('CI', None) == True, + reason='It only runs in CI services.') +def test_is_invalid_interpreter(): + assert not is_python_interpreter(INVALID_INTERPRETER) + + +def test_is_valid_interpreter_name(): + names = ['python', 'pythonw', 'python2.7', 'python3.5', 'python.exe', 'pythonw.exe'] + assert all([is_python_interpreter_valid_name(n) for n in names]) + + if __name__ == '__main__': pytest.main()