From 25c0ea7f61b740f828f4f0742fbec319b24eba8f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 19 Sep 2024 17:01:18 +0100 Subject: [PATCH] [PEP 695] Generate error if new-style type alias used as base class --- mypy/semanal.py | 15 ++++++++++++ test-data/unit/check-python312.test | 38 ++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 27d2a3abf93f..25a3aa0107f6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1824,6 +1824,8 @@ def analyze_class(self, defn: ClassDef) -> None: defn, bases, context=defn ) + self.check_type_alias_bases(bases) + for tvd in tvar_defs: if isinstance(tvd, TypeVarType) and any( has_placeholder(t) for t in [tvd.upper_bound] + tvd.values @@ -1895,6 +1897,19 @@ def analyze_class(self, defn: ClassDef) -> None: self.analyze_class_body_common(defn) + def check_type_alias_bases(self, bases: list[Expression]) -> None: + for base in bases: + if isinstance(base, IndexExpr): + base = base.base + if ( + isinstance(base, RefExpr) + and isinstance(base.node, TypeAlias) + and base.node.python_3_12_type_alias + ): + self.fail( + 'Type alias defined using "type" statement not valid as base class', base + ) + def setup_type_vars(self, defn: ClassDef, tvar_defs: list[TypeVarLikeType]) -> None: defn.type_vars = tvar_defs defn.info.type_vars = [] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index d0a39f7e56a6..292017b8b4f2 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -591,6 +591,40 @@ a4: A4 reveal_type(a4) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/type.pyi] +[case testPEP695TypeAliasNotValidAsBaseClass] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import TypeAlias + +import m + +type A1 = int +class Bad1(A1): # E: Type alias defined using "type" statement not valid as base class + pass + +type A2[T] = list[T] +class Bad2(A2[int]): # E: Type alias defined using "type" statement not valid as base class + pass + +class Bad3(m.A1): # E: Type alias defined using "type" statement not valid as base class + pass + +class Bad4(m.A2[int]): # E: Type alias defined using "type" statement not valid as base class + pass + +B1 = int +B2 = list +B3: TypeAlias = int +class Good1(B1): pass +class Good2(B2[int]): pass +class Good3(list[A1]): pass +class Good4(list[A2[int]]): pass +class Good5(B3): pass + +[file m.py] +type A1 = str +type A2[T] = list[T] +[typing fixtures/typing-medium.pyi] + [case testPEP695TypeAliasWithUnusedTypeParams] # flags: --enable-incomplete-feature=NewGenericSyntax type A[T] = int @@ -637,9 +671,7 @@ class D: pass type A = C -# Note that this doesn't actually work at runtime, but we currently don't -# keep track whether a type alias is valid in various runtime type contexts. -class D(A): +class D(A): # E: Type alias defined using "type" statement not valid as base class pass class C: pass