Skip to content

Commit 3801b7f

Browse files
authored
Drop support for --python-version 3.8 (#19157)
Drop last remaining support for Python 3.8. Support for running with 3.8 was removed in #17492 already. This PR removes the option to use 3.8 with `--python-version` since the type stubs only support 3.9+, see #18930.
1 parent f328ad6 commit 3801b7f

25 files changed

+86
-273
lines changed

mypy/defaults.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
# Earliest Python 3.x version supported via --python-version 3.x. To run
1212
# mypy, at least version PYTHON3_VERSION is needed.
13-
PYTHON3_VERSION_MIN: Final = (3, 8) # Keep in sync with typeshed's python support
13+
PYTHON3_VERSION_MIN: Final = (3, 9) # Keep in sync with typeshed's python support
1414

1515
CACHE_DIR: Final = ".mypy_cache"
1616

mypy/modulefinder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,6 @@ def parse_version(version: str) -> tuple[int, int]:
995995

996996
def typeshed_py_version(options: Options) -> tuple[int, int]:
997997
"""Return Python version used for checking whether module supports typeshed."""
998-
# Typeshed no longer covers Python 3.x versions before 3.8, so 3.8 is
998+
# Typeshed no longer covers Python 3.x versions before 3.9, so 3.9 is
999999
# the earliest we can support.
1000-
return max(options.python_version, (3, 8))
1000+
return max(options.python_version, (3, 9))

mypy/nodes.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,6 @@ def set_line(
148148
"builtins.frozenset": "typing.FrozenSet",
149149
}
150150

151-
_nongen_builtins: Final = {"builtins.tuple": "typing.Tuple", "builtins.enumerate": ""}
152-
_nongen_builtins.update((name, alias) for alias, name in type_aliases.items())
153-
# Drop OrderedDict from this for backward compatibility
154-
del _nongen_builtins["collections.OrderedDict"]
155-
# HACK: consequence of hackily treating LiteralString as an alias for str
156-
del _nongen_builtins["builtins.str"]
157-
158-
159-
def get_nongen_builtins(python_version: tuple[int, int]) -> dict[str, str]:
160-
# After 3.9 with pep585 generic builtins are allowed
161-
return _nongen_builtins if python_version < (3, 9) else {}
162-
163151

164152
RUNTIME_PROTOCOL_DECOS: Final = (
165153
"typing.runtime_checkable",

mypy/semanal.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@
184184
YieldExpr,
185185
YieldFromExpr,
186186
get_member_expr_fullname,
187-
get_nongen_builtins,
188187
implicit_module_attrs,
189188
is_final_node,
190189
type_aliases,
@@ -247,7 +246,6 @@
247246
find_self_type,
248247
fix_instance,
249248
has_any_from_unimported_type,
250-
no_subscript_builtin_alias,
251249
type_constructors,
252250
validate_instance,
253251
)
@@ -5996,30 +5994,6 @@ def analyze_type_application(self, expr: IndexExpr) -> None:
59965994
expr.analyzed = TypeApplication(base, types)
59975995
expr.analyzed.line = expr.line
59985996
expr.analyzed.column = expr.column
5999-
# Types list, dict, set are not subscriptable, prohibit this if
6000-
# subscripted either via type alias...
6001-
if isinstance(base, RefExpr) and isinstance(base.node, TypeAlias):
6002-
alias = base.node
6003-
target = get_proper_type(alias.target)
6004-
if isinstance(target, Instance):
6005-
name = target.type.fullname
6006-
if (
6007-
alias.no_args
6008-
and name # this avoids bogus errors for already reported aliases
6009-
in get_nongen_builtins(self.options.python_version)
6010-
and not self.is_stub_file
6011-
and not alias.normalized
6012-
):
6013-
self.fail(no_subscript_builtin_alias(name, propose_alt=False), expr)
6014-
# ...or directly.
6015-
else:
6016-
n = self.lookup_type_node(base)
6017-
if (
6018-
n
6019-
and n.fullname in get_nongen_builtins(self.options.python_version)
6020-
and not self.is_stub_file
6021-
):
6022-
self.fail(no_subscript_builtin_alias(n.fullname, propose_alt=False), expr)
60235997

60245998
def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None:
60255999
"""Analyze type arguments (index) in a type application.

mypy/typeanal.py

Lines changed: 7 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
Var,
4848
check_arg_kinds,
4949
check_arg_names,
50-
get_nongen_builtins,
5150
)
5251
from mypy.options import INLINE_TYPEDDICT, Options
5352
from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface
@@ -136,12 +135,6 @@
136135
"mypy_extensions.KwArg": ARG_STAR2,
137136
}
138137

139-
GENERIC_STUB_NOT_AT_RUNTIME_TYPES: Final = {
140-
"queue.Queue",
141-
"builtins._PathLike",
142-
"asyncio.futures.Future",
143-
}
144-
145138
SELF_TYPE_NAMES: Final = {"typing.Self", "typing_extensions.Self"}
146139

147140

@@ -186,17 +179,6 @@ def analyze_type_alias(
186179
return res, analyzer.aliases_used
187180

188181

189-
def no_subscript_builtin_alias(name: str, propose_alt: bool = True) -> str:
190-
class_name = name.split(".")[-1]
191-
msg = f'"{class_name}" is not subscriptable'
192-
# This should never be called if the python_version is 3.9 or newer
193-
nongen_builtins = get_nongen_builtins((3, 8))
194-
replacement = nongen_builtins[name]
195-
if replacement and propose_alt:
196-
msg += f', use "{replacement}" instead'
197-
return msg
198-
199-
200182
class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
201183
"""Semantic analyzer for types.
202184
@@ -360,14 +342,6 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
360342
hook = self.plugin.get_type_analyze_hook(fullname)
361343
if hook is not None:
362344
return hook(AnalyzeTypeContext(t, t, self))
363-
if (
364-
fullname in get_nongen_builtins(self.options.python_version)
365-
and t.args
366-
and not self.always_allow_new_syntax
367-
):
368-
self.fail(
369-
no_subscript_builtin_alias(fullname, propose_alt=not self.defining_alias), t
370-
)
371345
tvar_def = self.tvar_scope.get_binding(sym)
372346
if isinstance(sym.node, ParamSpecExpr):
373347
if tvar_def is None:
@@ -2033,44 +2007,14 @@ def get_omitted_any(
20332007
unexpanded_type: Type | None = None,
20342008
) -> AnyType:
20352009
if disallow_any:
2036-
nongen_builtins = get_nongen_builtins(options.python_version)
2037-
if fullname in nongen_builtins:
2038-
typ = orig_type
2039-
# We use a dedicated error message for builtin generics (as the most common case).
2040-
alternative = nongen_builtins[fullname]
2041-
fail(
2042-
message_registry.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative),
2043-
typ,
2044-
code=codes.TYPE_ARG,
2045-
)
2046-
else:
2047-
typ = unexpanded_type or orig_type
2048-
type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ, options)
2010+
typ = unexpanded_type or orig_type
2011+
type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ, options)
20492012

2050-
fail(
2051-
message_registry.BARE_GENERIC.format(quote_type_string(type_str)),
2052-
typ,
2053-
code=codes.TYPE_ARG,
2054-
)
2055-
base_type = get_proper_type(orig_type)
2056-
base_fullname = (
2057-
base_type.type.fullname if isinstance(base_type, Instance) else fullname
2058-
)
2059-
# Ideally, we'd check whether the type is quoted or `from __future__ annotations`
2060-
# is set before issuing this note
2061-
if (
2062-
options.python_version < (3, 9)
2063-
and base_fullname in GENERIC_STUB_NOT_AT_RUNTIME_TYPES
2064-
):
2065-
# Recommend `from __future__ import annotations` or to put type in quotes
2066-
# (string literal escaping) for classes not generic at runtime
2067-
note(
2068-
"Subscripting classes that are not generic at runtime may require "
2069-
"escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html"
2070-
"#not-generic-runtime",
2071-
typ,
2072-
code=codes.TYPE_ARG,
2073-
)
2013+
fail(
2014+
message_registry.BARE_GENERIC.format(quote_type_string(type_str)),
2015+
typ,
2016+
code=codes.TYPE_ARG,
2017+
)
20742018

20752019
any_type = AnyType(TypeOfAny.from_error, line=typ.line, column=typ.column)
20762020
else:

mypyc/test-data/run-misc.test

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -984,15 +984,6 @@ elif sys.version_info[:2] == (3, 10):
984984
elif sys.version_info[:2] == (3, 9):
985985
def version() -> int:
986986
return 9
987-
elif sys.version_info[:2] == (3, 8):
988-
def version() -> int:
989-
return 8
990-
elif sys.version_info[:2] == (3, 7):
991-
def version() -> int:
992-
return 7
993-
elif sys.version_info[:2] == (3, 6):
994-
def version() -> int:
995-
return 6
996987
else:
997988
raise Exception("we don't support this version yet!")
998989

mypyc/test/testutil.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def build_ir_for_single_file2(
111111
options.hide_error_codes = True
112112
options.use_builtins_fixtures = True
113113
options.strict_optional = True
114-
options.python_version = compiler_options.python_version or (3, 8)
114+
options.python_version = compiler_options.python_version or (3, 9)
115115
options.export_types = True
116116
options.preserve_asts = True
117117
options.allow_empty_bodies = True

test-data/unit/check-annotated.test

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,7 @@ def f4(a: Annotated[T, "metadata"]):
144144
reveal_type(f4) # N: Revealed type is "def [T] (a: T`-1) -> Any"
145145
[builtins fixtures/tuple.pyi]
146146

147-
[case testSliceAnnotated39]
148-
# flags: --python-version 3.9
149-
from typing_extensions import Annotated
150-
a: Annotated[int, 1:2]
151-
reveal_type(a) # N: Revealed type is "builtins.int"
152-
[builtins fixtures/tuple.pyi]
153-
154-
[case testSliceAnnotated38]
155-
# flags: --python-version 3.8
147+
[case testSliceAnnotated]
156148
from typing_extensions import Annotated
157149
a: Annotated[int, 1:2]
158150
reveal_type(a) # N: Revealed type is "builtins.int"

test-data/unit/check-columns.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,10 @@ class D(A):
261261
# N:5: def f(self) -> None
262262

263263
[case testColumnMissingTypeParameters]
264-
# flags: --python-version 3.8 --disallow-any-generics
264+
# flags: --disallow-any-generics
265265
from typing import List, Callable
266266
def f(x: List) -> None: pass # E:10: Missing type parameters for generic type "List"
267-
def g(x: list) -> None: pass # E:10: Implicit generic "Any". Use "typing.List" and specify generic parameters
267+
def g(x: list) -> None: pass # E:10: Missing type parameters for generic type "List"
268268
if int():
269269
c: Callable # E:8: Missing type parameters for generic type "Callable"
270270
[builtins fixtures/list.pyi]

test-data/unit/check-dataclasses.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1911,7 +1911,6 @@ SecondClass().SECOND_CONST = 42 # E: Cannot assign to final attribute "SECOND_C
19111911
[builtins fixtures/dataclasses.pyi]
19121912

19131913
[case testDataclassFieldsProtocol]
1914-
# flags: --python-version 3.9
19151914
from dataclasses import dataclass
19161915
from typing import Any, Protocol
19171916

test-data/unit/check-errorcodes.test

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,10 @@ a: A
341341
a.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
342342

343343
[case testErrorCodeMissingTypeArg]
344-
# flags: --python-version 3.8 --disallow-any-generics
344+
# flags: --disallow-any-generics
345345
from typing import List, TypeVar
346346
x: List # E: Missing type parameters for generic type "List" [type-arg]
347-
y: list # E: Implicit generic "Any". Use "typing.List" and specify generic parameters [type-arg]
347+
y: list # E: Missing type parameters for generic type "List" [type-arg]
348348
T = TypeVar('T')
349349
L = List[List[T]]
350350
z: L # E: Missing type parameters for generic type "L" [type-arg]
@@ -970,22 +970,21 @@ def f(arg: int) -> int:
970970
def f(arg: str) -> str:
971971
...
972972

973-
[case testSliceInDict39]
974-
# flags: --python-version 3.9 --show-column-numbers
975-
from typing import Dict
976-
b: Dict[int, x:y]
977-
c: Dict[x:y]
973+
[case testSliceInDictBuiltin]
974+
# flags: --show-column-numbers
975+
b: dict[int, x:y]
976+
c: dict[x:y]
978977

979978
[builtins fixtures/dict.pyi]
980979
[out]
981-
main:3:14: error: Invalid type comment or annotation [valid-type]
982-
main:3:14: note: did you mean to use ',' instead of ':' ?
983-
main:4:4: error: "dict" expects 2 type arguments, but 1 given [type-arg]
984-
main:4:9: error: Invalid type comment or annotation [valid-type]
985-
main:4:9: note: did you mean to use ',' instead of ':' ?
986-
987-
[case testSliceInDict38]
988-
# flags: --python-version 3.8 --show-column-numbers
980+
main:2:14: error: Invalid type comment or annotation [valid-type]
981+
main:2:14: note: did you mean to use ',' instead of ':' ?
982+
main:3:4: error: "dict" expects 2 type arguments, but 1 given [type-arg]
983+
main:3:9: error: Invalid type comment or annotation [valid-type]
984+
main:3:9: note: did you mean to use ',' instead of ':' ?
985+
986+
[case testSliceInDictTyping]
987+
# flags: --show-column-numbers
989988
from typing import Dict
990989
b: Dict[int, x:y]
991990
c: Dict[x:y]

test-data/unit/check-flags.test

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,16 +1501,14 @@ GroupsDict = Dict[str, GroupDataDict] # type: ignore
15011501

15021502

15031503
[case testCheckDisallowAnyGenericsStubOnly]
1504-
# flags: --disallow-any-generics --python-version 3.8
1504+
# flags: --disallow-any-generics
15051505
from asyncio import Future
15061506
from queue import Queue
15071507
x: Future[str]
15081508
y: Queue[int]
15091509

1510-
p: Future # E: Missing type parameters for generic type "Future" \
1511-
# N: Subscripting classes that are not generic at runtime may require escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html#not-generic-runtime
1512-
q: Queue # E: Missing type parameters for generic type "Queue" \
1513-
# N: Subscripting classes that are not generic at runtime may require escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html#not-generic-runtime
1510+
p: Future # E: Missing type parameters for generic type "Future"
1511+
q: Queue # E: Missing type parameters for generic type "Queue"
15141512
[file asyncio/__init__.pyi]
15151513
from asyncio.futures import Future as Future
15161514
[file asyncio/futures.pyi]
@@ -1524,28 +1522,28 @@ class Queue(Generic[_T]): ...
15241522
[builtins fixtures/async_await.pyi]
15251523
[typing fixtures/typing-full.pyi]
15261524

1527-
[case testDisallowAnyGenericsBuiltinTuplePre39]
1528-
# flags: --disallow-any-generics --python-version 3.8
1525+
[case testDisallowAnyGenericsBuiltinTuple]
1526+
# flags: --disallow-any-generics
15291527
s = tuple([1, 2, 3])
1530-
def f(t: tuple) -> None: pass # E: Implicit generic "Any". Use "typing.Tuple" and specify generic parameters
1528+
def f(t: tuple) -> None: pass # E: Missing type parameters for generic type "tuple"
15311529
[builtins fixtures/tuple.pyi]
15321530

1533-
[case testDisallowAnyGenericsBuiltinListPre39]
1534-
# flags: --disallow-any-generics --python-version 3.8
1531+
[case testDisallowAnyGenericsBuiltinList]
1532+
# flags: --disallow-any-generics
15351533
l = list([1, 2, 3])
1536-
def f(t: list) -> None: pass # E: Implicit generic "Any". Use "typing.List" and specify generic parameters
1534+
def f(t: list) -> None: pass # E: Missing type parameters for generic type "List"
15371535
[builtins fixtures/list.pyi]
15381536

1539-
[case testDisallowAnyGenericsBuiltinSetPre39]
1540-
# flags: --disallow-any-generics --python-version 3.8
1537+
[case testDisallowAnyGenericsBuiltinSet]
1538+
# flags: --disallow-any-generics
15411539
l = set({1, 2, 3})
1542-
def f(s: set) -> None: pass # E: Implicit generic "Any". Use "typing.Set" and specify generic parameters
1540+
def f(s: set) -> None: pass # E: Missing type parameters for generic type "Set"
15431541
[builtins fixtures/set.pyi]
15441542

1545-
[case testDisallowAnyGenericsBuiltinDictPre39]
1546-
# flags: --disallow-any-generics --python-version 3.8
1543+
[case testDisallowAnyGenericsBuiltinDict]
1544+
# flags: --disallow-any-generics
15471545
l = dict([('a', 1)])
1548-
def f(d: dict) -> None: pass # E: Implicit generic "Any". Use "typing.Dict" and specify generic parameters
1546+
def f(d: dict) -> None: pass # E: Missing type parameters for generic type "Dict"
15491547
[builtins fixtures/dict.pyi]
15501548

15511549
[case testCheckDefaultAllowAnyGeneric]

test-data/unit/check-functions.test

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,7 +1827,6 @@ def Arg(x, y): pass
18271827
F = Callable[[Arg(int, 'x')], int] # E: Invalid argument constructor "__main__.Arg"
18281828

18291829
[case testCallableParsingFromExpr]
1830-
# flags: --python-version 3.9
18311830
from typing import Callable, List
18321831
from mypy_extensions import Arg, VarArg, KwArg
18331832
import mypy_extensions
@@ -1858,13 +1857,6 @@ Q = Callable[[Arg(int, type=int)], int] # E: Invalid type alias: expression is
18581857
R = Callable[[Arg(int, 'x', name='y')], int] # E: Invalid type alias: expression is not a valid type \
18591858
# E: Value of type "int" is not indexable \
18601859
# E: "Arg" gets multiple values for keyword argument "name"
1861-
1862-
1863-
1864-
1865-
1866-
1867-
18681860
[builtins fixtures/dict.pyi]
18691861

18701862
[case testCallableParsing]

0 commit comments

Comments
 (0)