From 380d5d34b258e5a32e1f48c326a8d302c2365599 Mon Sep 17 00:00:00 2001 From: Vikash Kumar Date: Sun, 22 Feb 2026 13:41:27 +0530 Subject: [PATCH 1/5] stubtest: check Final variables with literal values against runtime --- mypy/stubtest.py | 8 ++++++++ mypy/test/teststubtest.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a6780984c1f54..55f1635bf44ed 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1279,6 +1279,14 @@ def verify_var( yield Error(object_path, "is not present at runtime", stub, runtime) return + if stub.final_value is not None and stub.final_value != runtime: + yield Error( + object_path, + f"is inconsistent, stub value {stub.final_value!r} differs from runtime value {runtime!r}", + stub, + runtime, + ) + if ( stub.is_initialized_in_class and is_read_only_property(runtime) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 45cc46c401082..a2b03236565b2 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1171,6 +1171,22 @@ def read_write_attr(self, val): self._val = val """, error=None, ) + yield Case( + stub=""" + from typing import Final + x_final: Final = 2 + """, + runtime="x_final = 1", + error="x_final", + ) + yield Case( + stub=""" + from typing import Final + x_final_ok: Final = 1 + """, + runtime="x_final_ok = 1", + error=None, + ) @collect_cases def test_type_alias(self) -> Iterator[Case]: From 2cc6b430de88ee24bb603d9bb4fc70b60016a712 Mon Sep 17 00:00:00 2001 From: Vikash Kumar <163628932+Vikash-Kumar-23@users.noreply.github.com> Date: Sun, 22 Feb 2026 20:46:26 +0530 Subject: [PATCH 2/5] Update mypy/stubtest.py Co-authored-by: Brian Schubert --- mypy/stubtest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 55f1635bf44ed..d822215a6fdf9 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1285,6 +1285,7 @@ def verify_var( f"is inconsistent, stub value {stub.final_value!r} differs from runtime value {runtime!r}", stub, runtime, + stub_desc=repr(stub.final_value), ) if ( From b997e14344e23e83985651e714856acf3c37b11c Mon Sep 17 00:00:00 2001 From: Vikash Kumar Date: Sun, 22 Feb 2026 21:55:19 +0530 Subject: [PATCH 3/5] Refactor: Move Final check to elif branch and clean error string --- mypy/stubtest.py | 17 ++++++++--------- mypy/test/teststubtest.py | 10 +++++----- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index d822215a6fdf9..02c92a5f55ebb 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1279,15 +1279,6 @@ def verify_var( yield Error(object_path, "is not present at runtime", stub, runtime) return - if stub.final_value is not None and stub.final_value != runtime: - yield Error( - object_path, - f"is inconsistent, stub value {stub.final_value!r} differs from runtime value {runtime!r}", - stub, - runtime, - stub_desc=repr(stub.final_value), - ) - if ( stub.is_initialized_in_class and is_read_only_property(runtime) @@ -1332,6 +1323,14 @@ def verify_var( stub, runtime, ) + elif stub.final_value is not None and stub.final_value != runtime: + yield Error( + object_path, + f"is inconsistent, stub value {stub.final_value!r} differs from runtime value", + stub, + runtime, + stub_desc=repr(stub.final_value), + ) @verify.register(nodes.OverloadedFuncDef) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index a2b03236565b2..dc3af77dcb5a1 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1174,17 +1174,17 @@ def read_write_attr(self, val): self._val = val yield Case( stub=""" from typing import Final - x_final: Final = 2 + X_FINAL: Final = 2 """, - runtime="x_final = 1", - error="x_final", + runtime="X_FINAL = 1", + error="X_FINAL", ) yield Case( stub=""" from typing import Final - x_final_ok: Final = 1 + X_FINAL_OK: Final = 1 """, - runtime="x_final_ok = 1", + runtime="X_FINAL_OK = 1", error=None, ) From 8949fd4fb8407642b3e67895a1ad780e13eeac14 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 22 Feb 2026 13:40:16 -0800 Subject: [PATCH 4/5] Update mypy/stubtest.py --- mypy/stubtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 02c92a5f55ebb..d32d318e4c9f1 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1326,7 +1326,7 @@ def verify_var( elif stub.final_value is not None and stub.final_value != runtime: yield Error( object_path, - f"is inconsistent, stub value {stub.final_value!r} differs from runtime value", + f"is inconsistent, stub value for Final var differs from runtime value", stub, runtime, stub_desc=repr(stub.final_value), From 8d63f8ef968b02c457e6cd2b2089e538743be5f3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Feb 2026 21:42:36 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/stubtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index d32d318e4c9f1..ae1da96af92bb 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1326,7 +1326,7 @@ def verify_var( elif stub.final_value is not None and stub.final_value != runtime: yield Error( object_path, - f"is inconsistent, stub value for Final var differs from runtime value", + "is inconsistent, stub value for Final var differs from runtime value", stub, runtime, stub_desc=repr(stub.final_value),