diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5bd20f2773c59..2b3e2f40d86ae 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,37 +26,26 @@ jobs: fail-fast: false matrix: include: - - name: Test suite with py37-windows-64 - python: '3.7' - arch: x64 - os: windows-latest - toxenv: py37 - - name: Test suite with py38-ubuntu + # Make sure to run mypyc compiled unit tests for both + # the oldest and newest supported Python versions + - name: Test suite with py38-ubuntu, mypyc-compiled python: '3.8' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 2" + test_mypyc: true + - name: Test suite with py38-windows-64 + python: '3.8' + arch: x64 + os: windows-latest + toxenv: py38 - name: Test suite with py39-ubuntu python: '3.9' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 2" - - name: Test suite with py37-ubuntu, mypyc-compiled - python: '3.7' - arch: x64 - os: ubuntu-latest - toxenv: py - tox_extra_args: "-n 2" - test_mypyc: true - - name: Test suite with py310-ubuntu, mypyc-compiled - python: '3.10' - arch: x64 - os: ubuntu-latest - toxenv: py - tox_extra_args: "-n 2" - test_mypyc: true - name: Test suite with py310-ubuntu python: '3.10' arch: x64 @@ -70,29 +59,32 @@ jobs: toxenv: py tox_extra_args: "-n 2" test_mypyc: true - - name: mypyc runtime tests with py37-macos - python: '3.7' + + - name: mypyc runtime tests with py38-macos + python: '3.8' arch: x64 os: macos-latest toxenv: py tox_extra_args: "-n 2 mypyc/test/test_run.py mypyc/test/test_external.py" - - name: mypyc runtime tests with py37-debug-build-ubuntu - python: '3.7.13' + - name: mypyc runtime tests with py38-debug-build-ubuntu + python: '3.8' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 2 mypyc/test/test_run.py mypyc/test/test_external.py" debug_build: true - - name: Type check our own code (py37-ubuntu) - python: '3.7' + + - name: Type check our own code (py38-ubuntu) + python: '3.8' arch: x64 os: ubuntu-latest toxenv: type - - name: Type check our own code (py37-windows-64) - python: '3.7' + - name: Type check our own code (py38-windows-64) + python: '3.8' arch: x64 os: windows-latest toxenv: type + # We also run these checks with pre-commit in CI, # but it's useful to run them with tox too, # to ensure the tox env works as expected diff --git a/mypy/defaults.py b/mypy/defaults.py index d167997464f44..59a99cd58888c 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -8,7 +8,7 @@ # Earliest fully supported Python 3.x version. Used as the default Python # version in tests. Mypy wheels should be built starting with this version, # and CI tests should be run on this version (and later versions). -PYTHON3_VERSION: Final = (3, 7) +PYTHON3_VERSION: Final = (3, 8) # Earliest Python 3.x version supported via --python-version 3.x. To run # mypy, at least version PYTHON3_VERSION is needed. diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 54544f4c01ce4..2427f83c57674 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -896,8 +896,6 @@ def cmd_inspect( force_reload: bool = False, ) -> dict[str, object]: """Locate and inspect expression(s).""" - if sys.version_info < (3, 8): - return {"error": 'Python 3.8 required for "inspect" command'} if not self.fine_grained_manager: return { "error": 'Command "inspect" is only valid after a "check" command' diff --git a/mypy/fastparse.py b/mypy/fastparse.py index fe59ff48bdfcf..f62016944fddd 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -118,123 +118,68 @@ ) from mypy.util import bytes_to_human_readable_repr, unnamed_function -try: - # pull this into a final variable to make mypyc be quiet about the - # the default argument warning - PY_MINOR_VERSION: Final = sys.version_info[1] - - # Check if we can use the stdlib ast module instead of typed_ast. - if sys.version_info >= (3, 8): - import ast as ast3 - - assert ( - "kind" in ast3.Constant._fields - ), f"This 3.8.0 alpha ({sys.version.split()[0]}) is too old; 3.8.0a3 required" - # TODO: Num, Str, Bytes, NameConstant, Ellipsis are deprecated in 3.8. - # TODO: Index, ExtSlice are deprecated in 3.9. - from ast import ( - AST, - Attribute, - Bytes, - Call, - Ellipsis as ast3_Ellipsis, - Expression as ast3_Expression, - FunctionType, - Index, - Name, - NameConstant, - Num, - Starred, - Str, - UnaryOp, - USub, - ) +# pull this into a final variable to make mypyc be quiet about the +# the default argument warning +PY_MINOR_VERSION: Final = sys.version_info[1] + +# Check if we can use the stdlib ast module instead of typed_ast. +import ast as ast3 + +# TODO: Index, ExtSlice are deprecated in 3.9. +from ast import ( + AST, + Attribute, + Call, + FunctionType, + Index, + Name, + Starred, + UnaryOp, + USub, +) - def ast3_parse( - source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION - ) -> AST: - return ast3.parse( - source, - filename, - mode, - type_comments=True, # This works the magic - feature_version=feature_version, - ) - NamedExpr = ast3.NamedExpr - Constant = ast3.Constant - else: - from typed_ast import ast3 - from typed_ast.ast3 import ( - AST, - Attribute, - Bytes, - Call, - Ellipsis as ast3_Ellipsis, - Expression as ast3_Expression, - FunctionType, - Index, - Name, - NameConstant, - Num, - Starred, - Str, - UnaryOp, - USub, - ) +def ast3_parse( + source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION +) -> AST: + return ast3.parse( + source, + filename, + mode, + type_comments=True, # This works the magic + feature_version=feature_version, + ) - def ast3_parse( - source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION - ) -> AST: - return ast3.parse(source, filename, mode, feature_version=feature_version) - - # These don't exist before 3.8 - NamedExpr = Any - Constant = Any - - if sys.version_info >= (3, 10): - Match = ast3.Match - MatchValue = ast3.MatchValue - MatchSingleton = ast3.MatchSingleton - MatchSequence = ast3.MatchSequence - MatchStar = ast3.MatchStar - MatchMapping = ast3.MatchMapping - MatchClass = ast3.MatchClass - MatchAs = ast3.MatchAs - MatchOr = ast3.MatchOr - AstNode = Union[ast3.expr, ast3.stmt, ast3.pattern, ast3.ExceptHandler] - else: - Match = Any - MatchValue = Any - MatchSingleton = Any - MatchSequence = Any - MatchStar = Any - MatchMapping = Any - MatchClass = Any - MatchAs = Any - MatchOr = Any - AstNode = Union[ast3.expr, ast3.stmt, ast3.ExceptHandler] - if sys.version_info >= (3, 11): - TryStar = ast3.TryStar - else: - TryStar = Any -except ImportError: - try: - from typed_ast import ast35 # type: ignore[attr-defined] # noqa: F401 - except ImportError: - print( - "The typed_ast package is not installed.\n" - "You can install it with `python3 -m pip install typed-ast`.", - file=sys.stderr, - ) - else: - print( - "You need a more recent version of the typed_ast package.\n" - "You can update to the latest version with " - "`python3 -m pip install -U typed-ast`.", - file=sys.stderr, - ) - sys.exit(1) + +NamedExpr = ast3.NamedExpr +Constant = ast3.Constant + +if sys.version_info >= (3, 10): + Match = ast3.Match + MatchValue = ast3.MatchValue + MatchSingleton = ast3.MatchSingleton + MatchSequence = ast3.MatchSequence + MatchStar = ast3.MatchStar + MatchMapping = ast3.MatchMapping + MatchClass = ast3.MatchClass + MatchAs = ast3.MatchAs + MatchOr = ast3.MatchOr + AstNode = Union[ast3.expr, ast3.stmt, ast3.pattern, ast3.ExceptHandler] +else: + Match = Any + MatchValue = Any + MatchSingleton = Any + MatchSequence = Any + MatchStar = Any + MatchMapping = Any + MatchClass = Any + MatchAs = Any + MatchOr = Any + AstNode = Union[ast3.expr, ast3.stmt, ast3.ExceptHandler] +if sys.version_info >= (3, 11): + TryStar = ast3.TryStar +else: + TryStar = Any N = TypeVar("N", bound=Node) @@ -370,7 +315,7 @@ def parse_type_comment( raise SyntaxError else: ignored = None - assert isinstance(typ, ast3_Expression) + assert isinstance(typ, ast3.Expression) converted = TypeConverter( errors, line=line, override_column=column, is_evaluated=False ).visit(typ.body) @@ -961,8 +906,10 @@ def do_func_def( func_type_ast = ast3_parse(n.type_comment, "", "func_type") assert isinstance(func_type_ast, FunctionType) # for ellipsis arg - if len(func_type_ast.argtypes) == 1 and isinstance( - func_type_ast.argtypes[0], ast3_Ellipsis + if ( + len(func_type_ast.argtypes) == 1 + and isinstance(func_type_ast.argtypes[0], Constant) + and func_type_ast.argtypes[0].value is Ellipsis ): if n.returns: # PEP 484 disallows both type annotations and type comments @@ -1052,15 +999,9 @@ def do_func_def( func_type.line = lineno if n.decorator_list: - if sys.version_info < (3, 8): - # Before 3.8, [typed_]ast the line number points to the first decorator. - # In 3.8, it points to the 'def' line, where we want it. - deco_line = lineno - lineno += len(n.decorator_list) # this is only approximately true - else: - # Set deco_line to the old pre-3.8 lineno, in order to keep - # existing "# type: ignore" comments working: - deco_line = n.decorator_list[0].lineno + # Set deco_line to the old pre-3.8 lineno, in order to keep + # existing "# type: ignore" comments working: + deco_line = n.decorator_list[0].lineno var = Var(func_def.name) var.is_ready = False @@ -1188,12 +1129,9 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: cdef.decorators = self.translate_expr_list(n.decorator_list) # Set lines to match the old mypy 0.700 lines, in order to keep # existing "# type: ignore" comments working: - if sys.version_info < (3, 8): - cdef.line = n.lineno + len(n.decorator_list) - cdef.deco_line = n.lineno - else: - cdef.line = n.lineno - cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None + cdef.line = n.lineno + cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None + cdef.column = n.col_offset cdef.end_line = getattr(n, "end_lineno", None) cdef.end_column = getattr(n, "end_col_offset", None) @@ -1580,9 +1518,9 @@ def visit_Constant(self, n: Constant) -> Any: if val is None: e = NameExpr("None") elif isinstance(val, str): - e = StrExpr(n.s) + e = StrExpr(val) elif isinstance(val, bytes): - e = BytesExpr(bytes_to_human_readable_repr(n.s)) + e = BytesExpr(bytes_to_human_readable_repr(val)) elif isinstance(val, bool): # Must check before int! e = NameExpr(str(val)) elif isinstance(val, int): @@ -1597,28 +1535,6 @@ def visit_Constant(self, n: Constant) -> Any: raise RuntimeError("Constant not implemented for " + str(type(val))) return self.set_line(e, n) - # Num(object n) -- a number as a PyObject. - def visit_Num(self, n: ast3.Num) -> IntExpr | FloatExpr | ComplexExpr: - # The n field has the type complex, but complex isn't *really* - # a parent of int and float, and this causes isinstance below - # to think that the complex branch is always picked. Avoid - # this by throwing away the type. - val: object = n.n - if isinstance(val, int): - e: IntExpr | FloatExpr | ComplexExpr = IntExpr(val) - elif isinstance(val, float): - e = FloatExpr(val) - elif isinstance(val, complex): - e = ComplexExpr(val) - else: - raise RuntimeError("num not implemented for " + str(type(val))) - return self.set_line(e, n) - - # Str(string s) - def visit_Str(self, n: Str) -> StrExpr: - e = StrExpr(n.s) - return self.set_line(e, n) - # JoinedStr(expr* values) def visit_JoinedStr(self, n: ast3.JoinedStr) -> Expression: # Each of n.values is a str or FormattedValue; we just concatenate @@ -1643,7 +1559,7 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression: # to allow mypyc to support f-strings with format specifiers and conversions. val_exp = self.visit(n.value) val_exp.set_line(n.lineno, n.col_offset) - conv_str = "" if n.conversion is None or n.conversion < 0 else "!" + chr(n.conversion) + conv_str = "" if n.conversion < 0 else "!" + chr(n.conversion) format_string = StrExpr("{" + conv_str + ":{}}") format_spec_exp = self.visit(n.format_spec) if n.format_spec is not None else StrExpr("") format_string.set_line(n.lineno, n.col_offset) @@ -1654,21 +1570,6 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression: ) return self.set_line(result_expression, n) - # Bytes(bytes s) - def visit_Bytes(self, n: ast3.Bytes) -> BytesExpr | StrExpr: - e = BytesExpr(bytes_to_human_readable_repr(n.s)) - return self.set_line(e, n) - - # NameConstant(singleton value) - def visit_NameConstant(self, n: NameConstant) -> NameExpr: - e = NameExpr(str(n.value)) - return self.set_line(e, n) - - # Ellipsis - def visit_Ellipsis(self, n: ast3_Ellipsis) -> EllipsisExpr: - e = EllipsisExpr() - return self.set_line(e, n) - # Attribute(expr value, identifier attr, expr_context ctx) def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr: value = n.value @@ -1955,9 +1856,9 @@ def translate_argument_list(self, l: Sequence[ast3.expr]) -> TypeList: return TypeList([self.visit(e) for e in l], line=self.line) def _extract_argument_name(self, n: ast3.expr) -> str | None: - if isinstance(n, Str): - return n.s.strip() - elif isinstance(n, NameConstant) and str(n.value) == "None": + if isinstance(n, Constant) and isinstance(n.value, str): + return n.value.strip() + elif isinstance(n, Constant) and n.value is None: return None self.fail( message_registry.ARG_NAME_EXPECTED_STRING_LITERAL.format(type(n).__name__), @@ -1983,12 +1884,6 @@ def visit_BinOp(self, n: ast3.BinOp) -> Type: uses_pep604_syntax=True, ) - def visit_NameConstant(self, n: NameConstant) -> Type: - if isinstance(n.value, bool): - return RawExpressionType(n.value, "builtins.bool", line=self.line) - else: - return UnboundType(str(n.value), line=self.line, column=n.col_offset) - # Only for 3.8 and newer def visit_Constant(self, n: Constant) -> Type: val = n.value @@ -2041,23 +1936,6 @@ def numeric_type(self, value: object, n: AST) -> Type: numeric_value, type_name, line=self.line, column=getattr(n, "col_offset", -1) ) - # These next three methods are only used if we are on python < - # 3.8, using typed_ast. They are defined unconditionally because - # mypyc can't handle conditional method definitions. - - # Num(number n) - def visit_Num(self, n: Num) -> Type: - return self.numeric_type(n.n, n) - - # Str(string s) - def visit_Str(self, n: Str) -> Type: - return parse_type_string(n.s, "builtins.str", self.line, n.col_offset) - - # Bytes(bytes s) - def visit_Bytes(self, n: Bytes) -> Type: - contents = bytes_to_human_readable_repr(n.s) - return RawExpressionType(contents, "builtins.bytes", self.line, column=n.col_offset) - def visit_Index(self, n: ast3.Index) -> Type: # cast for mypyc's benefit on Python 3.9 value = self.visit(cast(Any, n).value) @@ -2086,10 +1964,10 @@ def visit_Subscript(self, n: ast3.Subscript) -> Type: for s in dims: if getattr(s, "col_offset", None) is None: if isinstance(s, ast3.Index): - s.col_offset = s.value.col_offset # type: ignore[attr-defined] + s.col_offset = s.value.col_offset elif isinstance(s, ast3.Slice): assert s.lower is not None - s.col_offset = s.lower.col_offset # type: ignore[attr-defined] + s.col_offset = s.lower.col_offset sliceval = ast3.Tuple(dims, n.ctx) empty_tuple_index = False @@ -2130,10 +2008,6 @@ def visit_Attribute(self, n: Attribute) -> Type: else: return self.invalid_type(n) - # Ellipsis - def visit_Ellipsis(self, n: ast3_Ellipsis) -> Type: - return EllipsisType(line=self.line) - # List(expr* elts, expr_context ctx) def visit_List(self, n: ast3.List) -> Type: assert isinstance(n.ctx, ast3.Load) diff --git a/mypy/messages.py b/mypy/messages.py index ea7923c597782..8e59e73291682 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -235,6 +235,8 @@ def span_from_context(ctx: Context) -> Iterable[int]: Current logic is a bit tricky, to keep as much backwards compatibility as possible. We may reconsider this to always be a single line (or otherwise simplify it) when we drop Python 3.7. + + TODO: address this in follow up PR """ if isinstance(ctx, (ClassDef, FuncDef)): return range(ctx.deco_line or ctx.line, ctx.line + 1) diff --git a/mypy/pyinfo.py b/mypy/pyinfo.py index 778b0b163ce6d..f262ac8b2132c 100644 --- a/mypy/pyinfo.py +++ b/mypy/pyinfo.py @@ -2,10 +2,10 @@ """Utilities to find the site and prefix information of a Python executable. -This file MUST remain compatible with all Python 3.7+ versions. Since we cannot make any assumptions about the -Python being executed, this module should not use *any* dependencies outside of the standard -library found in Python 3.7. This file is run each mypy run, so it should be kept as fast as -possible. +This file MUST remain compatible with all Python 3.8+ versions. Since we cannot make any +assumptions about the Python being executed, this module should not use *any* dependencies outside +of the standard library found in Python 3.8. This file is run each mypy run, so it should be kept +as fast as possible. """ import sys diff --git a/mypy/test/meta/test_update_data.py b/mypy/test/meta/test_update_data.py index 67f9a7f56ebd2..4e4bdd193dbfa 100644 --- a/mypy/test/meta/test_update_data.py +++ b/mypy/test/meta/test_update_data.py @@ -29,10 +29,7 @@ def _run_pytest_update_data(self, data_suite: str, *, max_attempts: int) -> str: test_nodeid = f"mypy/test/testcheck.py::TypeCheckSuite::{p.name}" args = [sys.executable, "-m", "pytest", "-n", "0", "-s", "--update-data", test_nodeid] - if sys.version_info >= (3, 8): - cmd = shlex.join(args) - else: - cmd = " ".join(args) + cmd = shlex.join(args) for i in range(max_attempts - 1, -1, -1): res = subprocess.run(args, cwd=p_root) if res.returncode == 0: diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 58c0ee8033592..20dfbb77f3e04 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -36,9 +36,7 @@ # Includes all check-* files with the .test extension in the test-data/unit directory typecheck_files = find_test_files(pattern="check-*.test") -# Tests that use Python 3.8-only AST features (like expression-scoped ignores): -if sys.version_info < (3, 8): - typecheck_files.remove("check-python38.test") +# Tests that use Python version specific features: if sys.version_info < (3, 9): typecheck_files.remove("check-python39.test") if sys.version_info < (3, 10): diff --git a/mypy/test/testdaemon.py b/mypy/test/testdaemon.py index e3cdf44d89f2f..7115e682e60da 100644 --- a/mypy/test/testdaemon.py +++ b/mypy/test/testdaemon.py @@ -13,8 +13,6 @@ import tempfile import unittest -import pytest - from mypy.dmypy_server import filter_out_missing_top_level_packages from mypy.fscache import FileSystemCache from mypy.modulefinder import SearchPaths @@ -30,8 +28,6 @@ class DaemonSuite(DataSuite): files = daemon_files def run_case(self, testcase: DataDrivenTestCase) -> None: - if testcase.name.endswith("_python38") and sys.version_info < (3, 8): - pytest.skip("Not supported on this version of Python") try: test_daemon(testcase) finally: diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 5b4c816b5c384..d5ea65510eb2c 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -16,7 +16,6 @@ import os import re -import sys import unittest from typing import Any @@ -70,9 +69,6 @@ def should_skip(self, testcase: DataDrivenTestCase) -> bool: else: if testcase.only_when == "-only_when_cache": return True - - if "Inspect" in testcase.name and sys.version_info < (3, 8): - return True return False def run_case(self, testcase: DataDrivenTestCase) -> None: diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index da5025faef038..48d0658cd1e91 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -98,11 +98,6 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: assert testcase.old_cwd is not None, "test was not properly set up" python = sys.executable - if sys.version_info < (3, 8) and testcase.location[-1] == "testTypedPkgSimpleEditable": - # Python 3.7 doesn't ship with new enough pip to support PEP 660 - # This is a quick hack to skip the test; we'll drop Python 3.7 support soon enough - return - assert python is not None, "Should be impossible" pkgs, pip_args = parse_pkgs(testcase.input[0]) mypy_args = parse_mypy_args(testcase.input[1]) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 275b09c3a240e..661d46e9fd8af 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -232,17 +232,16 @@ def test_arg_name(self) -> Iterator[Case]: runtime="def bad(num, text) -> None: pass", error="bad", ) - if sys.version_info >= (3, 8): - yield Case( - stub="def good_posonly(__number: int, text: str) -> None: ...", - runtime="def good_posonly(num, /, text): pass", - error=None, - ) - yield Case( - stub="def bad_posonly(__number: int, text: str) -> None: ...", - runtime="def bad_posonly(flag, /, text): pass", - error="bad_posonly", - ) + yield Case( + stub="def good_posonly(__number: int, text: str) -> None: ...", + runtime="def good_posonly(num, /, text): pass", + error=None, + ) + yield Case( + stub="def bad_posonly(__number: int, text: str) -> None: ...", + runtime="def bad_posonly(flag, /, text): pass", + error="bad_posonly", + ) yield Case( stub=""" class BadMethod: @@ -283,22 +282,21 @@ def test_arg_kind(self) -> Iterator[Case]: runtime="def stub_posonly(number, text): pass", error="stub_posonly", ) - if sys.version_info >= (3, 8): - yield Case( - stub="def good_posonly(__number: int, text: str) -> None: ...", - runtime="def good_posonly(number, /, text): pass", - error=None, - ) - yield Case( - stub="def runtime_posonly(number: int, text: str) -> None: ...", - runtime="def runtime_posonly(number, /, text): pass", - error="runtime_posonly", - ) - yield Case( - stub="def stub_posonly_570(number: int, /, text: str) -> None: ...", - runtime="def stub_posonly_570(number, text): pass", - error="stub_posonly_570", - ) + yield Case( + stub="def good_posonly(__number: int, text: str) -> None: ...", + runtime="def good_posonly(number, /, text): pass", + error=None, + ) + yield Case( + stub="def runtime_posonly(number: int, text: str) -> None: ...", + runtime="def runtime_posonly(number, /, text): pass", + error="runtime_posonly", + ) + yield Case( + stub="def stub_posonly_570(number: int, /, text: str) -> None: ...", + runtime="def stub_posonly_570(number, text): pass", + error="stub_posonly_570", + ) @collect_cases def test_default_presence(self) -> Iterator[Case]: @@ -582,17 +580,16 @@ def f4(a: str, *args, b: int, **kwargs) -> str: ... runtime="def f4(a, *args, b, **kwargs): pass", error=None, ) - if sys.version_info >= (3, 8): - yield Case( - stub=""" - @overload - def f5(__a: int) -> int: ... - @overload - def f5(__b: str) -> str: ... - """, - runtime="def f5(x, /): pass", - error=None, - ) + yield Case( + stub=""" + @overload + def f5(__a: int) -> int: ... + @overload + def f5(__b: str) -> str: ... + """, + runtime="def f5(x, /): pass", + error=None, + ) @collect_cases def test_property(self) -> Iterator[Case]: diff --git a/mypy/util.py b/mypy/util.py index 2c225c7fe6510..2960818d0984a 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -421,10 +421,10 @@ def get_unique_redefinition_name(name: str, existing: Container[str]) -> str: def check_python_version(program: str) -> None: """Report issues with the Python used to run mypy, dmypy, or stubgen""" # Check for known bad Python versions. - if sys.version_info[:2] < (3, 7): + if sys.version_info[:2] < (3, 8): sys.exit( - "Running {name} with Python 3.6 or lower is not supported; " - "please upgrade to 3.7 or newer".format(name=program) + "Running {name} with Python 3.7 or lower is not supported; " + "please upgrade to 3.8 or newer".format(name=program) ) diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 62083d1446214..7413e6407d4fd 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -6,7 +6,7 @@ show_traceback = True pretty = True always_false = MYPYC plugins = misc/proper_plugin.py -python_version = 3.7 +python_version = 3.8 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr show_error_code_links = True diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index a4071b9c3de53..7573cd5c86969 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -67,8 +67,7 @@ ] files.append("run-python37.test") -if sys.version_info >= (3, 8): - files.append("run-python38.test") +files.append("run-python38.test") if sys.version_info >= (3, 10): files.append("run-match.test") diff --git a/setup.py b/setup.py index 85d412540013e..8d5dfd5438dfd 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,8 @@ import sys from typing import TYPE_CHECKING, Any -if sys.version_info < (3, 7, 0): - sys.stderr.write("ERROR: You need Python 3.7 or later to use mypy.\n") +if sys.version_info < (3, 8, 0): + sys.stderr.write("ERROR: You need Python 3.8 or later to use mypy.\n") exit(1) # we'll import stuff from the source tree, let's ensure is on the sys path @@ -186,7 +186,6 @@ def run(self): "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -221,9 +220,7 @@ def run(self): cmdclass=cmdclass, # When changing this, also update mypy-requirements.txt. install_requires=[ - "typed_ast >= 1.4.0, < 2; python_version<'3.8'", - "typing_extensions>=4.1.0; python_version >= '3.8'", - "typing_extensions>=4.7.0; python_version < '3.8'", + "typing_extensions>=4.1.0", "mypy_extensions >= 1.0.0", "tomli>=1.1.0; python_version<'3.11'", ], @@ -234,7 +231,7 @@ def run(self): "reports": "lxml", "install-types": "pip", }, - python_requires=">=3.7", + python_requires=">=3.8", include_package_data=True, project_urls={ "News": "https://mypy-lang.org/news.html",