Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
54 changes: 53 additions & 1 deletion mypy/nativeparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,9 @@


class State:
def __init__(self, options: Options) -> None:
def __init__(self, options: Options, is_stub: bool = False) -> None:
self.options = options
self.is_stub = is_stub
self.errors: list[ParseError] = []
self.num_funcs = 0

Expand All @@ -180,6 +181,29 @@ def add_error(
{"line": line, "column": column, "message": message, "blocker": blocker, "code": code}
)

def check_min_version(
self,
feature: str,
min_version: tuple[int, int],
line: int,
column: int,
*,
enforce_in_stubs: bool = False,
) -> None:
"""Report a non blocker syntax error if the target Python feature is older than min_version."""
if self.is_stub and not enforce_in_stubs:
return
if self.options.python_version < min_version:
curr = self.options.python_version
self.add_error(
f"{feature}: requires Python {min_version[0]}.{min_version[1]} or newer "
f"(current target: Python {curr[0]}.{curr[1]})",
line,
column,
blocker=False,
code="syntax",
)
Comment thread
KevinRK29 marked this conversation as resolved.


def native_parse(
filename: str,
Expand Down Expand Up @@ -607,6 +631,13 @@ def read_parameters(state: State, data: ReadBuffer) -> tuple[list[Argument], boo
return arguments, has_ann


def check_type_param_defaults(
state: State, type_params: list[TypeParam], line: int, column: int
) -> None:
if any(p.default is not None for p in type_params):
state.check_min_version("Type parameter defaults", (3, 13), line, column)


def read_type_params(state: State, data: ReadBuffer) -> list[TypeParam]:
"""Read type parameters (PEP 695 generics)."""
type_params: list[TypeParam] = []
Expand Down Expand Up @@ -680,6 +711,11 @@ def read_func_def(state: State, data: ReadBuffer) -> FuncDef:
if is_async:
func_def.is_coroutine = True
read_loc(data, func_def)
if type_params:
state.check_min_version(
"Improved type parameter syntax", (3, 12), func_def.line, func_def.column
)
check_type_param_defaults(state, type_params, func_def.line, func_def.column)
if typ:
typ.line = func_def.line
typ.column = func_def.column
Expand Down Expand Up @@ -727,6 +763,11 @@ def read_class_def(state: State, data: ReadBuffer) -> ClassDef:
)
class_def.decorators = decorators
read_loc(data, class_def)
if type_params:
state.check_min_version(
"Improved type parameter syntax", (3, 12), class_def.line, class_def.column
)
check_type_param_defaults(state, type_params, class_def.line, class_def.column)
expect_end_tag(data)
return class_def

Expand Down Expand Up @@ -781,6 +822,8 @@ def read_type_alias_stmt(state: State, data: ReadBuffer) -> TypeAliasStmt:

stmt = TypeAliasStmt(name, type_params, lambda_expr)
read_loc(data, stmt)
state.check_min_version('"type" statements', (3, 12), stmt.line, stmt.column)
check_type_param_defaults(state, type_params, stmt.line, stmt.column)
expect_end_tag(data)
return stmt

Expand Down Expand Up @@ -832,6 +875,10 @@ def read_try_stmt(state: State, data: ReadBuffer) -> TryStmt:
stmt = TryStmt(body, vars_list, types_list, handlers, else_body, finally_body)
stmt.is_star = is_star
read_loc(data, stmt)
if is_star:
state.check_min_version("Exception groups", (3, 11), stmt.line, stmt.column)
if state.options.python_version < (3, 11):
stmt.is_star = False
expect_end_tag(data)
return stmt

Expand Down Expand Up @@ -967,6 +1014,8 @@ def read_type(state: State, data: ReadBuffer) -> Type:
from_star_syntax = read_bool(data)
unpack = UnpackType(inner_type, from_star_syntax=from_star_syntax)
read_loc(data, unpack)
if from_star_syntax:
state.check_min_version("Star unpack syntax", (3, 11), unpack.line, unpack.column)
expect_end_tag(data)
return unpack
elif tag == types.CALL_TYPE:
Expand Down Expand Up @@ -1474,6 +1523,9 @@ def read_expression(state: State, data: ReadBuffer) -> Expression:
titems.append(s)
expr = TemplateStrExpr(titems)
read_loc(data, expr)
state.check_min_version(
"t-strings", (3, 14), expr.line, expr.column, enforce_in_stubs=True
)
expect_end_tag(data)
return expr
elif tag == nodes.LAMBDA_EXPR:
Expand Down
2 changes: 1 addition & 1 deletion mypy/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def load_from_raw(
"""
from mypy.nativeparse import State, deserialize_imports, read_statements

state = State(options)
state = State(options, is_stub=fnam.endswith(".pyi"))
if imports_only:
defs = []
else:
Expand Down
8 changes: 7 additions & 1 deletion mypy/test/test_nativeparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@
class NativeParserSuite(DataSuite):
required_out_section = True
base_path = "."
files = ["native-parser.test"] if has_nativeparse else []
files = (
["native-parser.test", "native-parser-python311.test", "native-parser-python312.test"]
if has_nativeparse
else []
)

def run_case(self, testcase: DataDrivenTestCase) -> None:
test_parser(testcase)
Expand All @@ -77,6 +81,8 @@ def test_parser(testcase: DataDrivenTestCase) -> None:

if testcase.file.endswith("python310.test"):
options.python_version = (3, 10)
elif testcase.file.endswith("python311.test"):
options.python_version = (3, 11)
elif testcase.file.endswith("python312.test"):
options.python_version = (3, 12)
elif testcase.file.endswith("python313.test"):
Expand Down
4 changes: 4 additions & 0 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ reveal_type(b) # N: Revealed type is "def (*Any) -> builtins.int"
[builtins fixtures/tuple.pyi]

[case testTypeVarTuplePep646CallableNewSyntax]
# flags: --python-version 3.11
from typing import Callable, Generic, Tuple
from typing_extensions import ParamSpec

Expand Down Expand Up @@ -2155,6 +2156,7 @@ def f(x: int | None):
[builtins fixtures/tuple.pyi]

[case testJoinOfVariadicTupleCallablesNoCrash]
# flags: --python-version 3.11
from typing import Callable, Tuple

f: Callable[[int, *Tuple[str, ...], int], None]
Expand Down Expand Up @@ -2608,6 +2610,7 @@ def test(xs: tuple[Unpack[Ts]], xsi: tuple[int, Unpack[Ts]]) -> None:
[builtins fixtures/tuple.pyi]

[case testTypeVarTupleInferAgainstAnyCallableSuffix]
# flags: --python-version 3.11
from typing import Any, Callable, TypeVar, TypeVarTuple

Ts = TypeVarTuple("Ts")
Expand All @@ -2620,6 +2623,7 @@ reveal_type(deco(untyped)) # N: Revealed type is "def (*Any) -> Any"
[builtins fixtures/tuple.pyi]

[case testNoCrashOnNonNormalUnpackInCallable]
# flags: --python-version 3.11
from typing import Callable, Unpack, TypeVar

T = TypeVar("T")
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/check-varargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,7 @@ class D:
[builtins fixtures/dict.pyi]

[case testUnpackInCallableType]
# flags: --python-version 3.11
from typing import Callable, TypedDict
from typing_extensions import Unpack

Expand Down
108 changes: 108 additions & 0 deletions test-data/unit/native-parser-python311.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
[case testTryExceptStar]
try:
x
except* ValueError:
y
[out]
MypyFile:1(
TryStmt:1(
Block:2(
ExpressionStmt:2(
NameExpr(x)))
*
NameExpr(ValueError)
Block:4(
ExpressionStmt:4(
NameExpr(y)))))

[case testTryExceptStarWithVar]
try:
x
except* Exception as e:
y
[out]
MypyFile:1(
TryStmt:1(
Block:2(
ExpressionStmt:2(
NameExpr(x)))
*
NameExpr(Exception)
NameExpr(e)
Block:4(
ExpressionStmt:4(
NameExpr(y)))))

[case testTryMultipleExceptStar]
try:
x
except* ValueError:
y
except* KeyError as e:
z
[out]
MypyFile:1(
TryStmt:1(
Block:2(
ExpressionStmt:2(
NameExpr(x)))
*
NameExpr(ValueError)
Block:4(
ExpressionStmt:4(
NameExpr(y)))
NameExpr(KeyError)
NameExpr(e)
Block:6(
ExpressionStmt:6(
NameExpr(z)))))

[case testTryExceptStarElseFinally]
try:
x
except* ValueError:
y
else:
z
finally:
w
[out]
MypyFile:1(
TryStmt:1(
Block:2(
ExpressionStmt:2(
NameExpr(x)))
*
NameExpr(ValueError)
Block:4(
ExpressionStmt:4(
NameExpr(y)))
Else(
ExpressionStmt:6(
NameExpr(z)))
Finally(
ExpressionStmt:8(
NameExpr(w)))))

[case testUnpackTypeInSignature]
def f(*args: *Ts) -> None:
pass
[out]
MypyFile:1(
FuncDef:1(
f
def (*args: *Ts?) -> None?
VarArg(
Var(args))
Block:2(
PassStmt:2())))

[case testUnpackTypeInTuple]
x: tuple[int, *Ts, str]
[out]
MypyFile:1(
AssignmentStmt:1(
NameExpr(x)
TempNode:1(
Any)
tuple?[int?, *Ts?, str?]))
90 changes: 90 additions & 0 deletions test-data/unit/native-parser-python312.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
[case testPEP695TypeAlias]
# comment
type A[T] = C[T]
[out]
MypyFile:1(
TypeAliasStmt:2(
NameExpr(A)
TypeParam(
T)
LambdaExpr:2(
Block:-1(
ReturnStmt:2(
IndexExpr:2(
NameExpr(C)
NameExpr(T)))))))

[case testPEP695GenericFunction]
# comment

def f[T](): pass
def g[T: str](): pass
def h[T: (int, str)](): pass
[out]
MypyFile:1(
FuncDef:3(
f
TypeParam(
T)
Block:3(
PassStmt:3()))
FuncDef:4(
g
TypeParam(
T
str?)
Block:4(
PassStmt:4()))
FuncDef:5(
h
TypeParam(
T
Values(
int?
str?))
Block:5(
PassStmt:5())))

[case testPEP695ParamSpec]
# comment

def f[**P](): pass
class C[T: int, **P]: pass
[out]
MypyFile:1(
FuncDef:3(
f
TypeParam(
**P)
Block:3(
PassStmt:3()))
ClassDef:4(
C
TypeParam(
T
int?)
TypeParam(
**P)
PassStmt:4()))

[case testPEP695TypeVarTuple]
# comment

def f[*Ts](): pass
class C[T: int, *Ts]: pass
[out]
MypyFile:1(
FuncDef:3(
f
TypeParam(
*Ts)
Block:3(
PassStmt:3()))
ClassDef:4(
C
TypeParam(
T
int?)
TypeParam(
*Ts)
PassStmt:4()))
Loading
Loading