Skip to content

Commit bd1f51a

Browse files
authored
[mypyc] Show the reason why a class can't be a native class (#19016)
1 parent 6d860cf commit bd1f51a

File tree

2 files changed

+22
-11
lines changed

2 files changed

+22
-11
lines changed

mypyc/irbuild/util.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,15 @@ def is_extension_class(path: str, cdef: ClassDef, errors: Errors) -> bool:
134134
if explicit_native_class is False:
135135
return False
136136

137-
implicit_extension_class = is_implicit_extension_class(cdef)
137+
implicit_extension_class, reason = is_implicit_extension_class(cdef)
138138

139139
# Classes with native_class=True should be extension classes, but they might
140140
# not be able to be due to other reasons. Print an error in that case.
141141
if explicit_native_class is True and not implicit_extension_class:
142142
errors.error(
143-
"Class is marked as native_class=True but it can't be a native class", path, cdef.line
143+
f"Class is marked as native_class=True but it can't be a native class. {reason}",
144+
path,
145+
cdef.line,
144146
)
145147

146148
return implicit_extension_class
@@ -177,28 +179,37 @@ def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool
177179
return None
178180

179181

180-
def is_implicit_extension_class(cdef: ClassDef) -> bool:
182+
def is_implicit_extension_class(cdef: ClassDef) -> tuple[bool, str]:
183+
"""Check if class can be extension class and return a user-friendly reason it can't be one."""
184+
181185
for d in cdef.decorators:
182-
# Classes that have any decorator other than supported decorators, are not extension classes
183186
if (
184187
not is_trait_decorator(d)
185188
and not is_dataclass_decorator(d)
186189
and not get_mypyc_attr_call(d)
187190
and not is_final_decorator(d)
188191
):
189-
return False
192+
return (
193+
False,
194+
"Classes that have decorators other than supported decorators"
195+
" can't be native classes.",
196+
)
190197

191198
if cdef.info.typeddict_type:
192-
return False
199+
return False, "TypedDict classes can't be native classes."
193200
if cdef.info.is_named_tuple:
194-
return False
201+
return False, "NamedTuple classes can't be native classes."
195202
if cdef.info.metaclass_type and cdef.info.metaclass_type.type.fullname not in (
196203
"abc.ABCMeta",
197204
"typing.TypingMeta",
198205
"typing.GenericMeta",
199206
):
200-
return False
201-
return True
207+
return (
208+
False,
209+
"Classes with a metaclass other than ABCMeta, TypingMeta or"
210+
" GenericMeta can't be native classes.",
211+
)
212+
return True, ""
202213

203214

204215
def get_func_def(op: FuncDef | Decorator | OverloadedFuncDef) -> FuncDef:

mypyc/test-data/irbuild-classes.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,7 +1363,7 @@ def decorator(cls):
13631363

13641364
@mypyc_attr(native_class=True)
13651365
@decorator
1366-
class NonNativeClassContradiction(): # E: Class is marked as native_class=True but it can't be a native class
1366+
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.
13671367
pass
13681368

13691369

@@ -1379,5 +1379,5 @@ class M(type): # E: Inheriting from most builtin types is unimplemented
13791379
pass
13801380

13811381
@mypyc_attr(native_class=True)
1382-
class A(metaclass=M): # E: Class is marked as native_class=True but it can't be a native class
1382+
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.
13831383
pass

0 commit comments

Comments
 (0)