From 29fdba381ec951d63320be6975ce5552e44def64 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Tue, 26 Mar 2019 01:43:17 -0700 Subject: [PATCH] Drop support for running with 3.4 --- .travis.yml | 3 --- README.md | 2 +- docs/source/common_issues.rst | 2 +- docs/source/getting_started.rst | 2 +- mypy/dmypy_server.py | 2 +- mypy/main.py | 6 ++--- mypy/test/helpers.py | 18 ------------- mypy/test/testpep561.py | 23 ++++++++-------- mypy/test/testpythoneval.py | 8 +++--- mypy/util.py | 47 --------------------------------- runtests.py | 2 +- setup.py | 6 ++--- 12 files changed, 27 insertions(+), 94 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1a95050cd5be..227213fdebc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,6 @@ env: jobs: include: - - name: "run test suite with python 3.4" - python: 3.4 - dist: trusty # Specifically request 3.5.1 because we need to be compatible with that. - name: "run test suite with python 3.5.1" python: 3.5.1 diff --git a/README.md b/README.md index eac41ddbef50..758a458fee92 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ See 'Development status' below. Requirements ------------ -You need Python 3.4 or later to run mypy. You can have multiple Python +You need Python 3.5 or later to run mypy. You can have multiple Python versions (2.x and 3.x) installed on the same system without problems. In Ubuntu, Mint and Debian you can install Python 3 like this: diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 1c73f7ae4347..a4da8ec13546 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -14,7 +14,7 @@ Can't install mypy using pip If installation fails, you've probably hit one of these issues: -* Mypy needs Python 3.4 or later to run. +* Mypy needs Python 3.5 or later to run. * You may have to run pip like this: ``python3 -m pip install mypy``. diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index b5157101568c..fd206491f88a 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -12,7 +12,7 @@ may not make much sense otherwise. Installing and running mypy *************************** -Mypy requires Python 3.4 or later to run. Once you've +Mypy requires Python 3.5 or later to run. Once you've `installed Python 3 `_, install mypy using pip: diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 79e5769fee2e..3b464a80afab 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -14,6 +14,7 @@ import sys import time import traceback +from contextlib import redirect_stderr, redirect_stdout from typing import AbstractSet, Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple @@ -30,7 +31,6 @@ from mypy.options import Options from mypy.suggestions import SuggestionFailure, SuggestionEngine from mypy.typestate import reset_global_state -from mypy.util import redirect_stderr, redirect_stdout from mypy.version import __version__ diff --git a/mypy/main.py b/mypy/main.py index ab23f90c7fde..325a9c91608d 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -54,9 +54,9 @@ def main(script_path: Optional[str], args: Optional[List[str]] = None) -> None: be used. """ # Check for known bad Python versions. - if sys.version_info[:2] < (3, 4): - sys.exit("Running mypy with Python 3.3 or lower is not supported; " - "please upgrade to 3.4 or newer") + if sys.version_info[:2] < (3, 5): + sys.exit("Running mypy with Python 3.4 or lower is not supported; " + "please upgrade to 3.5 or newer") if sys.version_info[:3] == (3, 5, 0): sys.exit("Running mypy with Python 3.5.0 is not supported; " "please upgrade to 3.5.1 or newer") diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index f23e3d739735..7a3ea0a831da 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -397,24 +397,6 @@ def split_lines(*streams: bytes) -> List[str]: ] -def run_command(cmdline: List[str], *, env: Optional[Dict[str, str]] = None, - timeout: int = 300, cwd: str = test_temp_dir) -> Tuple[int, List[str]]: - """A poor man's subprocess.run() for 3.4 compatibility.""" - process = subprocess.Popen( - cmdline, - env=env, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - cwd=cwd, - ) - try: - out, err = process.communicate(timeout=timeout) - except subprocess.TimeoutExpired: - out = err = b'' - process.kill() - return process.returncode, split_lines(out, err) - - def copy_and_fudge_mtime(source_path: str, target_path: str) -> None: # In some systems, mtime has a resolution of 1 second which can # cause annoying-to-debug issues when a file has the same size diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index ed0e56e55181..4b988503df79 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -1,6 +1,8 @@ from contextlib import contextmanager from enum import Enum import os +import subprocess +from subprocess import PIPE import sys import tempfile from typing import Tuple, List, Generator, Optional @@ -9,7 +11,6 @@ import mypy.api from mypy.modulefinder import get_site_packages_dirs from mypy.test.config import package_path -from mypy.test.helpers import run_command from mypy.util import try_find_python2_interpreter # NOTE: options.use_builtins_fixtures should not be set in these @@ -135,13 +136,13 @@ def virtualenv(self, # Sadly, we need virtualenv, as the Python 3 venv module does not support creating a venv # for Python 2, and Python 2 does not have its own venv. with tempfile.TemporaryDirectory() as venv_dir: - returncode, lines = run_command([sys.executable, - '-m', - 'virtualenv', - '-p{}'.format(python_executable), - venv_dir], cwd=os.getcwd()) - if returncode != 0: - err = '\n'.join(lines) + proc = subprocess.run([sys.executable, + '-m', + 'virtualenv', + '-p{}'.format(python_executable), + venv_dir], cwd=os.getcwd(), stdout=PIPE, stderr=PIPE) + if proc.returncode != 0: + err = proc.stdout.decode('utf-8') + proc.stderr.decode('utf-8') self.fail("Failed to create venv. Do you have virtualenv installed?\n" + err) if sys.platform == 'win32': yield venv_dir, os.path.abspath(os.path.join(venv_dir, 'Scripts', 'python')) @@ -165,9 +166,9 @@ def install_package(self, pkg: str, install_cmd.append('develop') else: install_cmd.append('install') - returncode, lines = run_command(install_cmd, cwd=working_dir) - if returncode != 0: - self.fail('\n'.join(lines)) + proc = subprocess.run(install_cmd, cwd=working_dir, stdout=PIPE, stderr=PIPE) + if proc.returncode != 0: + self.fail(proc.stdout.decode('utf-8') + proc.stderr.decode('utf-8')) def setUp(self) -> None: self.simple_prog = ExampleProg(SIMPLE_PROGRAM) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index bd507e630e8b..89a3afaca110 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -13,6 +13,8 @@ import os import os.path import re +import subprocess +from subprocess import PIPE import sys from tempfile import TemporaryDirectory @@ -23,7 +25,7 @@ from mypy.defaults import PYTHON3_VERSION from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite -from mypy.test.helpers import assert_string_arrays_equal, run_command +from mypy.test.helpers import assert_string_arrays_equal, split_lines from mypy.util import try_find_python2_interpreter from mypy import api @@ -90,8 +92,8 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None output.append(line.rstrip("\r\n")) if returncode == 0: # Execute the program. - returncode, interp_out = run_command([interpreter, program]) - output.extend(interp_out) + proc = subprocess.run([interpreter, program], cwd=test_temp_dir, stdout=PIPE, stderr=PIPE) + output.extend(split_lines(proc.stdout, proc.stderr)) # Remove temp file. os.remove(program_path) for i, line in enumerate(output): diff --git a/mypy/util.py b/mypy/util.py index df054814df4e..3455e9c8e70b 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -257,50 +257,3 @@ def hard_exit(status: int = 0) -> None: def unmangle(name: str) -> str: """Remove internal suffixes from a short name.""" return name.rstrip("'") - - -# The following is a backport of stream redirect utilities from Lib/contextlib.py -# We need this for 3.4 support. They can be removed in March 2019! - - -class _RedirectStream: - - _stream = None # type: ClassVar[str] - - def __init__(self, new_target: TextIO) -> None: - self._new_target = new_target - # We use a list of old targets to make this CM re-entrant - self._old_targets = [] # type: List[TextIO] - - def __enter__(self) -> TextIO: - self._old_targets.append(getattr(sys, self._stream)) - setattr(sys, self._stream, self._new_target) - return self._new_target - - def __exit__(self, - exc_ty: 'Optional[Type[BaseException]]' = None, - exc_val: Optional[BaseException] = None, - exc_tb: Optional[TracebackType] = None, - ) -> bool: - setattr(sys, self._stream, self._old_targets.pop()) - return False - - -class redirect_stdout(_RedirectStream): - """Context manager for temporarily redirecting stdout to another file. - # How to send help() to stderr - with redirect_stdout(sys.stderr): - help(dir) - # How to write help() to a file - with open('help.txt', 'w') as f: - with redirect_stdout(f): - help(pow) - """ - - _stream = "stdout" - - -class redirect_stderr(_RedirectStream): - """Context manager for temporarily redirecting stderr to another file.""" - - _stream = "stderr" diff --git a/runtests.py b/runtests.py index 9a2ac05a9115..36c2dfa2a014 100755 --- a/runtests.py +++ b/runtests.py @@ -6,7 +6,7 @@ # Use the Python provided to execute the script, or fall back to a sane default -if version_info >= (3, 4, 0): +if version_info >= (3, 5, 0): python_name = executable else: if platform == 'win32': diff --git a/setup.py b/setup.py index 07c085133e23..e3dc63889861 100644 --- a/setup.py +++ b/setup.py @@ -5,8 +5,8 @@ import os.path import sys -if sys.version_info < (3, 4, 0): - sys.stderr.write("ERROR: You need Python 3.4 or later to use mypy.\n") +if sys.version_info < (3, 5, 0): + sys.stderr.write("ERROR: You need Python 3.5 or later to use mypy.\n") exit(1) # we'll import stuff from the source tree, let's ensure is on the sys path @@ -150,7 +150,6 @@ def run(self): 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', @@ -179,7 +178,6 @@ def run(self): 'mypy_extensions >= 0.4.0, < 0.5.0', ], extras_require = { - ':python_version < "3.5"': 'typing >= 3.5.3', 'dmypy': 'psutil >= 5.4.0, < 5.5.0; sys_platform!="win32"', }, include_package_data=True,