From b0b25b3320fa7d20e1ab5dd1e9e3d38ab05af280 Mon Sep 17 00:00:00 2001 From: Kevin Kannammalil Date: Mon, 25 May 2026 03:44:54 -0400 Subject: [PATCH 1/3] add version checks --- mypy/nativeparse.py | 53 +++++++++++++++++++++++++++++++++++++++++++-- mypy/parse.py | 2 +- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/mypy/nativeparse.py b/mypy/nativeparse.py index 414426580fa7..cfef0defff7a 100644 --- a/mypy/nativeparse.py +++ b/mypy/nativeparse.py @@ -29,7 +29,7 @@ read_str as read_str_bare, ) -from mypy import message_registry, nodes, types +from mypy import defaults, message_registry, nodes, types from mypy.cache import ( DICT_STR_GEN, END_TAG, @@ -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 @@ -180,6 +181,28 @@ 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, + ) -> None: + """Report a non blocker syntax error if the target Python feature is older than min_version.""" + if self.is_stub: + target = max(self.options.python_version, defaults.PYTHON3_VERSION) + else: + target = self.options.python_version + if target < min_version: + self.add_error( + f"{feature} are only supported in Python " + f"{min_version[0]}.{min_version[1]} and greater", + line, + column, + blocker=False, + code="syntax", + ) + def native_parse( filename: str, @@ -607,6 +630,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] = [] @@ -680,6 +710,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( + "Type parameter lists", (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 @@ -727,6 +762,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( + "Type parameter lists", (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 @@ -781,6 +821,10 @@ 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 @@ -832,6 +876,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 @@ -1474,6 +1522,7 @@ 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) expect_end_tag(data) return expr elif tag == nodes.LAMBDA_EXPR: diff --git a/mypy/parse.py b/mypy/parse.py index a8fb5542a704..47e2f95f0f30 100644 --- a/mypy/parse.py +++ b/mypy/parse.py @@ -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: From 3a8407e954151772e1b3437f0eaa3011161e2858 Mon Sep 17 00:00:00 2001 From: Kevin Kannammalil Date: Mon, 25 May 2026 04:23:59 -0400 Subject: [PATCH 2/3] stub check update --- mypy/nativeparse.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mypy/nativeparse.py b/mypy/nativeparse.py index cfef0defff7a..2003f0620a63 100644 --- a/mypy/nativeparse.py +++ b/mypy/nativeparse.py @@ -29,7 +29,7 @@ read_str as read_str_bare, ) -from mypy import defaults, message_registry, nodes, types +from mypy import message_registry, nodes, types from mypy.cache import ( DICT_STR_GEN, END_TAG, @@ -190,10 +190,8 @@ def check_min_version( ) -> None: """Report a non blocker syntax error if the target Python feature is older than min_version.""" if self.is_stub: - target = max(self.options.python_version, defaults.PYTHON3_VERSION) - else: - target = self.options.python_version - if target < min_version: + return + if self.options.python_version < min_version: self.add_error( f"{feature} are only supported in Python " f"{min_version[0]}.{min_version[1]} and greater", From c8851c007e9be793fd27aeda8f79af7abfee6678 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 08:26:03 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/nativeparse.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/mypy/nativeparse.py b/mypy/nativeparse.py index 2003f0620a63..a5c91ca4bf3b 100644 --- a/mypy/nativeparse.py +++ b/mypy/nativeparse.py @@ -182,11 +182,7 @@ def add_error( ) def check_min_version( - self, - feature: str, - min_version: tuple[int, int], - line: int, - column: int, + self, feature: str, min_version: tuple[int, int], line: int, column: int ) -> None: """Report a non blocker syntax error if the target Python feature is older than min_version.""" if self.is_stub: @@ -709,9 +705,7 @@ def read_func_def(state: State, data: ReadBuffer) -> FuncDef: func_def.is_coroutine = True read_loc(data, func_def) if type_params: - state.check_min_version( - "Type parameter lists", (3, 12), func_def.line, func_def.column - ) + state.check_min_version("Type parameter lists", (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 @@ -761,9 +755,7 @@ 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( - "Type parameter lists", (3, 12), class_def.line, class_def.column - ) + state.check_min_version("Type parameter lists", (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 @@ -819,9 +811,7 @@ 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 - ) + 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