Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump PYTHON3_VERSION_MIN to 3.7 #15668

Merged
merged 7 commits into from
Jul 15, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -12,7 +12,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
sobolevn marked this conversation as resolved.
Show resolved Hide resolved
# 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