Skip to content

Commit

Permalink
Bump minimum Python type check target version to 3.7 (#15668)
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn authored Jul 15, 2023
1 parent 1958cb6 commit 14743a1
Show file tree
Hide file tree
Showing 33 changed files with 75 additions and 314 deletions.
2 changes: 1 addition & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3072,7 +3072,7 @@ def visit_op_expr(self, e: OpExpr) -> Type:
# Expressions of form [...] * e get special type inference.
return self.check_list_multiply(e)
if e.op == "%":
if isinstance(e.left, BytesExpr) and self.chk.options.python_version >= (3, 5):
if isinstance(e.left, BytesExpr):
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
if isinstance(e.left, StrExpr):
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
Expand Down
15 changes: 0 additions & 15 deletions mypy/checkstrformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,14 +682,6 @@ def check_str_interpolation(self, expr: FormatStringExpr, replacements: Expressi
self.exprchk.accept(expr)
specifiers = parse_conversion_specifiers(expr.value)
has_mapping_keys = self.analyze_conversion_specifiers(specifiers, expr)
if isinstance(expr, BytesExpr) and self.chk.options.python_version < (3, 5):
self.msg.fail(
"Bytes formatting is only supported in Python 3.5 and later",
replacements,
code=codes.STRING_FORMATTING,
)
return AnyType(TypeOfAny.from_error)

if has_mapping_keys is None:
pass # Error was reported
elif has_mapping_keys:
Expand Down Expand Up @@ -1023,13 +1015,6 @@ def conversion_type(
NUMERIC_TYPES = NUMERIC_TYPES_NEW if format_call else NUMERIC_TYPES_OLD
INT_TYPES = REQUIRE_INT_NEW if format_call else REQUIRE_INT_OLD
if p == "b" and not format_call:
if self.chk.options.python_version < (3, 5):
self.msg.fail(
'Format character "b" is only supported in Python 3.5 and later',
context,
code=codes.STRING_FORMATTING,
)
return None
if not isinstance(expr, BytesExpr):
self.msg.fail(
'Format character "b" is only supported on bytes patterns',
Expand Down
2 changes: 1 addition & 1 deletion mypy/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

# Earliest Python 3.x version supported via --python-version 3.x. To run
# mypy, at least version PYTHON3_VERSION is needed.
PYTHON3_VERSION_MIN: Final = (3, 4)
PYTHON3_VERSION_MIN: Final = (3, 7) # Keep in sync with typeshed's python support

CACHE_DIR: Final = ".mypy_cache"
CONFIG_FILE: Final = ["mypy.ini", ".mypy.ini"]
Expand Down
13 changes: 2 additions & 11 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1728,7 +1728,6 @@ def need_annotation_for_var(
self, node: SymbolNode, context: Context, python_version: tuple[int, int] | None = None
) -> None:
hint = ""
has_variable_annotations = not python_version or python_version >= (3, 6)
pep604_supported = not python_version or python_version >= (3, 10)
# type to recommend the user adds
recommended_type = None
Expand All @@ -1749,18 +1748,10 @@ def need_annotation_for_var(
type_dec = f"{type_dec}, {type_dec}"
recommended_type = f"{alias}[{type_dec}]"
if recommended_type is not None:
if has_variable_annotations:
hint = f' (hint: "{node.name}: {recommended_type} = ...")'
else:
hint = f' (hint: "{node.name} = ... # type: {recommended_type}")'

if has_variable_annotations:
needed = "annotation"
else:
needed = "comment"
hint = f' (hint: "{node.name}: {recommended_type} = ...")'

self.fail(
f'Need type {needed} for "{unmangle(node.name)}"{hint}',
f'Need type annotation for "{unmangle(node.name)}"{hint}',
context,
code=codes.VAR_ANNOTATED,
)
Expand Down
18 changes: 4 additions & 14 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2521,12 +2521,7 @@ def visit_import_from(self, imp: ImportFrom) -> None:
elif fullname in self.missing_modules:
missing_submodule = True
# If it is still not resolved, check for a module level __getattr__
if (
module
and not node
and (module.is_stub or self.options.python_version >= (3, 7))
and "__getattr__" in module.names
):
if module and not node and "__getattr__" in module.names:
# We store the fullname of the original definition so that we can
# detect whether two imported names refer to the same thing.
fullname = module_id + "." + id
Expand Down Expand Up @@ -5446,11 +5441,8 @@ def visit_yield_expr(self, e: YieldExpr) -> None:
blocker=True,
)
elif self.function_stack[-1].is_coroutine:
if self.options.python_version < (3, 6):
self.fail('"yield" in async function', e, serious=True, blocker=True)
else:
self.function_stack[-1].is_generator = True
self.function_stack[-1].is_async_generator = True
self.function_stack[-1].is_generator = True
self.function_stack[-1].is_async_generator = True
else:
self.function_stack[-1].is_generator = True
if e.expr:
Expand Down Expand Up @@ -5721,9 +5713,7 @@ def get_module_symbol(self, node: MypyFile, name: str) -> SymbolTableNode | None
sym = SymbolTableNode(GDEF, self.modules[fullname])
elif self.is_incomplete_namespace(module):
self.record_incomplete_ref()
elif "__getattr__" in names and (
node.is_stub or self.options.python_version >= (3, 7)
):
elif "__getattr__" in names:
gvar = self.create_getattr_var(names["__getattr__"], name, fullname)
if gvar:
sym = SymbolTableNode(GDEF, gvar)
Expand Down
3 changes: 0 additions & 3 deletions mypy/semanal_namedtuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,6 @@ def check_namedtuple_classdef(
* valid statements
or None, if any of the types are not ready.
"""
if self.options.python_version < (3, 6) and not is_stub_file:
self.fail("NamedTuple class syntax is only supported in Python 3.6", defn)
return [], [], {}, []
if len(defn.base_type_exprs) > 1:
self.fail("NamedTuple should be a single base", defn)
items: list[str] = []
Expand Down
7 changes: 3 additions & 4 deletions mypy/semanal_pass1.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ class SemanticAnalyzerPreAnalysis(TraverserVisitor):
import sys
def do_stuff():
# type: () -> None:
if sys.python_version < (3,):
import xyz # Only available in Python 2
def do_stuff() -> None:
if sys.version_info >= (3, 10):
import xyz # Only available in Python 3.10+
xyz.whatever()
...
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ feature you added. If you added a new `check-*.test` file, it will be autodiscov
Add the test in this format anywhere in the file:

[case testNewSyntaxBasics]
# flags: --python-version 3.6
# flags: --python-version 3.10
x: int
x = 5
y: int = 5
Expand Down
40 changes: 5 additions & 35 deletions test-data/unit/check-async-await.test
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ async def f() -> None:
[typing fixtures/typing-async.pyi]

[case testAsyncForComprehension]
# flags: --python-version 3.6
from typing import Generic, Iterable, TypeVar, AsyncIterator, Tuple

T = TypeVar('T')
Expand Down Expand Up @@ -223,7 +222,6 @@ async def generatorexp(obj: Iterable[int]):
[typing fixtures/typing-async.pyi]

[case testAsyncForComprehensionErrors]
# flags: --python-version 3.6
from typing import Generic, Iterable, TypeVar, AsyncIterator, Tuple

T = TypeVar('T')
Expand All @@ -240,16 +238,10 @@ class asyncify(Generic[T], AsyncIterator[T]):
raise StopAsyncIteration

async def wrong_iterable(obj: Iterable[int]):
[i async for i in obj]
[i for i in asyncify(obj)]
{i: i async for i in obj}
{i: i for i in asyncify(obj)}

[out]
main:18: error: "Iterable[int]" has no attribute "__aiter__" (not async iterable)
main:19: error: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable)
main:20: error: "Iterable[int]" has no attribute "__aiter__" (not async iterable)
main:21: error: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable)
[i async for i in obj] # E: "Iterable[int]" has no attribute "__aiter__" (not async iterable)
[i for i in asyncify(obj)] # E: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable)
{i: i async for i in obj} # E: "Iterable[int]" has no attribute "__aiter__" (not async iterable)
{i: i for i in asyncify(obj)} # E: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable)
[builtins fixtures/async_await.pyi]
[typing fixtures/typing-async.pyi]

Expand Down Expand Up @@ -340,17 +332,6 @@ async def f() -> None:
[builtins fixtures/async_await.pyi]
[typing fixtures/typing-async.pyi]

[case testNoYieldInAsyncDef]
# flags: --python-version 3.5

async def f():
yield None # E: "yield" in async function
async def g():
yield # E: "yield" in async function
async def h():
x = yield # E: "yield" in async function
[builtins fixtures/async_await.pyi]

[case testNoYieldFromInAsyncDef]

async def f():
Expand Down Expand Up @@ -422,7 +403,6 @@ def f() -> Generator[int, str, int]:
-- ---------------------------------------------------------------------

[case testAsyncGenerator]
# flags: --python-version 3.6
from typing import AsyncGenerator, Generator

async def f() -> int:
Expand Down Expand Up @@ -450,7 +430,6 @@ async def wrong_return() -> Generator[int, None, None]: # E: The return type of
[typing fixtures/typing-async.pyi]

[case testAsyncGeneratorReturnIterator]
# flags: --python-version 3.6
from typing import AsyncIterator

async def gen() -> AsyncIterator[int]:
Expand All @@ -466,7 +445,6 @@ async def use_gen() -> None:
[typing fixtures/typing-async.pyi]

[case testAsyncGeneratorManualIter]
# flags: --python-version 3.6
from typing import AsyncGenerator

async def genfunc() -> AsyncGenerator[int, None]:
Expand All @@ -484,7 +462,6 @@ async def user() -> None:
[typing fixtures/typing-async.pyi]

[case testAsyncGeneratorAsend]
# flags: --python-version 3.6
from typing import AsyncGenerator

async def f() -> None:
Expand All @@ -505,7 +482,6 @@ async def h() -> None:
[typing fixtures/typing-async.pyi]

[case testAsyncGeneratorAthrow]
# flags: --python-version 3.6
from typing import AsyncGenerator

async def gen() -> AsyncGenerator[str, int]:
Expand All @@ -524,25 +500,20 @@ async def h() -> None:
[typing fixtures/typing-async.pyi]

[case testAsyncGeneratorNoSyncIteration]
# flags: --python-version 3.6
from typing import AsyncGenerator

async def gen() -> AsyncGenerator[int, None]:
for i in [1, 2, 3]:
yield i

def h() -> None:
for i in gen():
for i in gen(): # E: "AsyncGenerator[int, None]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable)
pass

[builtins fixtures/dict.pyi]
[typing fixtures/typing-async.pyi]

[out]
main:9: error: "AsyncGenerator[int, None]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable)

[case testAsyncGeneratorNoYieldFrom]
# flags: --python-version 3.6
from typing import AsyncGenerator

async def f() -> AsyncGenerator[int, None]:
Expand All @@ -555,7 +526,6 @@ async def gen() -> AsyncGenerator[int, None]:
[typing fixtures/typing-async.pyi]

[case testAsyncGeneratorNoReturnWithValue]
# flags: --python-version 3.6
from typing import AsyncGenerator

async def return_int() -> AsyncGenerator[int, None]:
Expand Down
Loading

0 comments on commit 14743a1

Please sign in to comment.