From 84229e74e91d3ab9c93f586722d08bbd036003ab Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Thu, 11 Oct 2018 20:04:53 -0700 Subject: [PATCH 1/5] Remove check that python_[version/executable] agree --- mypy/main.py | 11 ++--------- mypy/test/testargs.py | 13 ------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index c2d803d9f00b..d158a0556417 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -265,15 +265,8 @@ def infer_python_version_and_executable(options: Options, # TODO: (ethanhs) Look at folding these checks and the site packages subprocess calls into # one subprocess call for speed. if special_opts.python_executable is not None and special_opts.python_version is not None: - py_exe_ver = _python_version_from_executable(special_opts.python_executable) - if py_exe_ver != special_opts.python_version: - raise PythonExecutableInferenceError( - 'Python version {} did not match executable {}, got version {}.'.format( - special_opts.python_version, special_opts.python_executable, py_exe_ver - )) - else: - options.python_version = special_opts.python_version - options.python_executable = special_opts.python_executable + options.python_version = special_opts.python_version + options.python_executable = special_opts.python_executable elif special_opts.python_executable is None and special_opts.python_version is not None: options.python_version = special_opts.python_version py_exe = None diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index bccbafaf4c8b..ac59e68668b6 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -47,19 +47,6 @@ def test_executable_inference(self) -> None: assert options.python_version == sys.version_info[:2] assert options.python_executable == sys.executable - # test that we error if the version mismatch - # argparse sys.exits on a parser.error, we need to check the raw inference function - options = Options() - - special_opts = argparse.Namespace() - special_opts.python_executable = sys.executable - special_opts.python_version = (2, 10) # obviously wrong - special_opts.no_executable = None - with pytest.raises(PythonExecutableInferenceError) as e: - infer_python_version_and_executable(options, special_opts) - assert str(e.value) == 'Python version (2, 10) did not match executable {}, got' \ - ' version {}.'.format(sys.executable, sys.version_info[:2]) - # test that --no-site-packages will disable executable inference matching_version = base + ['--python-version={}'.format(sys_ver_str), '--no-site-packages'] From 4518bab754ed5501ef5c8e9ebcd88c35dc3d8b27 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Thu, 11 Oct 2018 20:36:28 -0700 Subject: [PATCH 2/5] Allow setting python_version/executable from config file This fixes #5620 --- mypy/main.py | 24 +++++++++++++++--------- mypy/test/testargs.py | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index d158a0556417..dec4bae61953 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -264,19 +264,25 @@ def infer_python_version_and_executable(options: Options, # TODO: (ethanhs) Look at folding these checks and the site packages subprocess calls into # one subprocess call for speed. - if special_opts.python_executable is not None and special_opts.python_version is not None: - options.python_version = special_opts.python_version - options.python_executable = special_opts.python_executable - elif special_opts.python_executable is None and special_opts.python_version is not None: - options.python_version = special_opts.python_version + + # Use the command line specified python_version/executable, or fall back to one set in the + # config file + python_version = special_opts.python_version or options.python_version + python_executable = special_opts.python_executable or options.python_executable + + if python_executable is not None and python_version is not None: + options.python_version = python_version + options.python_executable = python_executable + elif python_executable is None and python_version is not None: + options.python_version = python_version py_exe = None if not special_opts.no_executable: - py_exe = _python_executable_from_version(special_opts.python_version) + py_exe = _python_executable_from_version(python_version) options.python_executable = py_exe - elif special_opts.python_version is None and special_opts.python_executable is not None: + elif python_version is None and python_executable is not None: options.python_version = _python_version_from_executable( - special_opts.python_executable) - options.python_executable = special_opts.python_executable + python_executable) + options.python_executable = python_executable HEADER = """%(prog)s [-h] [-v] [-V] [more options; see below] diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index ac59e68668b6..6b89eee268da 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -53,3 +53,22 @@ def test_executable_inference(self) -> None: _, options = process_options(matching_version) assert options.python_version == sys.version_info[:2] assert options.python_executable is None + + # Test setting python_version/executable from config file + special_opts = argparse.Namespace() + special_opts.python_executable = None + special_opts.python_version = None + special_opts.no_executable = None + options = Options() + # first test inferring executable from version + options.python_executable = None + options.python_version = sys.version_info[:2] + infer_python_version_and_executable(options, special_opts) + assert options.python_version == sys.version_info[:2] + assert options.python_executable == sys.executable + # then test inferring version from executable + options.python_version = None + options.python_executable = sys.executable + infer_python_version_and_executable(options, special_opts) + assert options.python_version == sys.version_info[:2] + assert options.python_executable == sys.executable From c3ac4376ff7da684335b13eb93b17cfa8104b9d3 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Tue, 23 Oct 2018 22:02:32 -0700 Subject: [PATCH 3/5] Fix test --- mypy/test/testargs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index 6b89eee268da..51be458acc90 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -59,15 +59,17 @@ def test_executable_inference(self) -> None: special_opts.python_executable = None special_opts.python_version = None special_opts.no_executable = None - options = Options() + # first test inferring executable from version + options = Options() options.python_executable = None options.python_version = sys.version_info[:2] infer_python_version_and_executable(options, special_opts) assert options.python_version == sys.version_info[:2] assert options.python_executable == sys.executable + # then test inferring version from executable - options.python_version = None + options = Options() options.python_executable = sys.executable infer_python_version_and_executable(options, special_opts) assert options.python_version == sys.version_info[:2] From d140f5ac949fe4bff6f6fa233c31d9babc5d4d4e Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Wed, 31 Oct 2018 18:14:54 -0700 Subject: [PATCH 4/5] Simplify to only do inference on the executable used --- mypy/main.py | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index dec4bae61953..e8e695ec2845 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -227,17 +227,6 @@ def python_executable_prefix(v: str) -> List[str]: return ['python{}'.format(v)] -def _python_version_from_executable(python_executable: str) -> Tuple[int, int]: - try: - check = subprocess.check_output([python_executable, '-c', - 'import sys; print(repr(sys.version_info[:2]))'], - stderr=subprocess.STDOUT).decode() - return ast.literal_eval(check) - except (subprocess.CalledProcessError, FileNotFoundError): - raise PythonExecutableInferenceError( - 'invalid Python executable {}'.format(python_executable)) - - def _python_executable_from_version(python_version: Tuple[int, int]) -> str: if sys.version_info[:2] == python_version: return sys.executable @@ -267,22 +256,14 @@ def infer_python_version_and_executable(options: Options, # Use the command line specified python_version/executable, or fall back to one set in the # config file - python_version = special_opts.python_version or options.python_version + + options.python_version = special_opts.python_version or options.python_version python_executable = special_opts.python_executable or options.python_executable - if python_executable is not None and python_version is not None: - options.python_version = python_version - options.python_executable = python_executable - elif python_executable is None and python_version is not None: - options.python_version = python_version - py_exe = None + if python_executable is None: if not special_opts.no_executable: - py_exe = _python_executable_from_version(python_version) - options.python_executable = py_exe - elif python_version is None and python_executable is not None: - options.python_version = _python_version_from_executable( - python_executable) - options.python_executable = python_executable + python_executable = _python_executable_from_version(options.python_version) + options.python_executable = python_executable HEADER = """%(prog)s [-h] [-v] [-V] [more options; see below] From 48bfa2bb5b3f4db1886b6eb8cf93d0dc219d4874 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Wed, 31 Oct 2018 18:32:23 -0700 Subject: [PATCH 5/5] Cleanup and renaming --- mypy/main.py | 24 ++++++++++++------------ mypy/test/testargs.py | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index ce13e03a2752..7b72825f4b7c 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -242,22 +242,19 @@ def _python_executable_from_version(python_version: Tuple[int, int]) -> str: ' perhaps try --python-executable, or --no-site-packages?'.format(python_version)) -def infer_python_version_and_executable(options: Options, - special_opts: argparse.Namespace) -> None: - """Infer the Python version or executable from each other. Check they are consistent. +def infer_python_executable(options: Options, + special_opts: argparse.Namespace) -> None: + """Infer the Python executable from the given version. - This function mutates options based on special_opts to infer the correct Python version and - executable to use. + This function mutates options based on special_opts to infer the correct Python executable + to use. """ - # Infer Python version and/or executable if one is not given - # TODO: (ethanhs) Look at folding these checks and the site packages subprocess calls into # one subprocess call for speed. - # Use the command line specified python_version/executable, or fall back to one set in the - # config file - - options.python_version = special_opts.python_version or options.python_version + # Use the command line specified executable, or fall back to one set in the + # config file. If an executable is not specified, infer it from the version + # (unless no_executable is set) python_executable = special_opts.python_executable or options.python_executable if python_executable is None: @@ -724,8 +721,11 @@ def add_invertible_flag(flag: str, print("Warning: --quick-and-dirty is deprecated. It will disappear in the next release.", file=sys.stderr) + # The python_version is either the default, which can be overridden via a config file, + # or stored in special_opts and is passed via the command line. + options.python_version = special_opts.python_version or options.python_version try: - infer_python_version_and_executable(options, special_opts) + infer_python_executable(options, special_opts) except PythonExecutableInferenceError as e: parser.error(str(e)) diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index 51be458acc90..7c1e9ef45c56 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -12,7 +12,7 @@ from mypy.test.helpers import Suite, assert_equal from mypy.options import Options from mypy.main import (process_options, PythonExecutableInferenceError, - infer_python_version_and_executable) + infer_python_executable) class ArgSuite(Suite): @@ -64,13 +64,13 @@ def test_executable_inference(self) -> None: options = Options() options.python_executable = None options.python_version = sys.version_info[:2] - infer_python_version_and_executable(options, special_opts) + infer_python_executable(options, special_opts) assert options.python_version == sys.version_info[:2] assert options.python_executable == sys.executable # then test inferring version from executable options = Options() options.python_executable = sys.executable - infer_python_version_and_executable(options, special_opts) + infer_python_executable(options, special_opts) assert options.python_version == sys.version_info[:2] assert options.python_executable == sys.executable