From db28023076e4a94a4f2e0f9566dd848275bbf1e4 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Fri, 2 May 2025 15:12:41 +0000 Subject: [PATCH 1/2] [mypyc] Show the reason why a class can't be a native class Addresses feedback in #18999 --- mypyc/irbuild/util.py | 31 +++++++++++++++++++--------- mypyc/test-data/irbuild-classes.test | 4 ++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index 939c543c85a2..798076eee16c 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any +from typing import Any, Tuple from mypy.nodes import ( ARG_NAMED, @@ -134,13 +134,15 @@ def is_extension_class(path: str, cdef: ClassDef, errors: Errors) -> bool: if explicit_native_class is False: return False - implicit_extension_class = is_implicit_extension_class(cdef) + implicit_extension_class, reason = is_implicit_extension_class(cdef) # Classes with native_class=True should be extension classes, but they might # not be able to be due to other reasons. Print an error in that case. if explicit_native_class is True and not implicit_extension_class: errors.error( - "Class is marked as native_class=True but it can't be a native class", path, cdef.line + f"Class is marked as native_class=True but it can't be a native class. {reason}", + path, + cdef.line, ) return implicit_extension_class @@ -177,28 +179,37 @@ def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool return None -def is_implicit_extension_class(cdef: ClassDef) -> bool: +def is_implicit_extension_class(cdef: ClassDef) -> Tuple[bool, str]: + """Check if class can be extension class and return a user-friendly reason it can't be one.""" + for d in cdef.decorators: - # Classes that have any decorator other than supported decorators, are not extension classes if ( not is_trait_decorator(d) and not is_dataclass_decorator(d) and not get_mypyc_attr_call(d) and not is_final_decorator(d) ): - return False + return ( + False, + "Classes that have decorators other than supported decorators" + " can't be native classes.", + ) if cdef.info.typeddict_type: - return False + return False, "TypedDict classes can't be native classes." if cdef.info.is_named_tuple: - return False + return False, "NamedTuple classes can't be native classes." if cdef.info.metaclass_type and cdef.info.metaclass_type.type.fullname not in ( "abc.ABCMeta", "typing.TypingMeta", "typing.GenericMeta", ): - return False - return True + return ( + False, + "Classes with a metaclass other than ABCMeta, TypingMeta or" + " GenericMeta can't be native classes.", + ) + return True, "" def get_func_def(op: FuncDef | Decorator | OverloadedFuncDef) -> FuncDef: diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 94971640a094..9d564a552a05 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1363,7 +1363,7 @@ def decorator(cls): @mypyc_attr(native_class=True) @decorator -class NonNativeClassContradiction(): # E: Class is marked as native_class=True but it can't be a native class +class NonNativeClassContradiction(): # E: Class is marked as native_class=True but it can't be a native class. Classes that have decorators other than supported decorators can't be native classes. pass @@ -1379,5 +1379,5 @@ class M(type): # E: Inheriting from most builtin types is unimplemented pass @mypyc_attr(native_class=True) -class A(metaclass=M): # E: Class is marked as native_class=True but it can't be a native class +class A(metaclass=M): # E: Class is marked as native_class=True but it can't be a native class. Classes with a metaclass other than ABCMeta, TypingMeta or GenericMeta can't be native classes. pass From 5f6cb7b56d3ef3f11185857542accb653ee4f1f6 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Fri, 2 May 2025 15:34:33 +0000 Subject: [PATCH 2/2] tuple, not Tuple --- mypyc/irbuild/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index 798076eee16c..757b49c68c83 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Tuple +from typing import Any from mypy.nodes import ( ARG_NAMED, @@ -179,7 +179,7 @@ def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool return None -def is_implicit_extension_class(cdef: ClassDef) -> Tuple[bool, str]: +def is_implicit_extension_class(cdef: ClassDef) -> tuple[bool, str]: """Check if class can be extension class and return a user-friendly reason it can't be one.""" for d in cdef.decorators: