|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | | -from typing import Any |
| 5 | +from typing import Any, Tuple |
6 | 6 |
|
7 | 7 | from mypy.nodes import ( |
8 | 8 | ARG_NAMED, |
@@ -134,13 +134,15 @@ def is_extension_class(path: str, cdef: ClassDef, errors: Errors) -> bool: |
134 | 134 | if explicit_native_class is False: |
135 | 135 | return False |
136 | 136 |
|
137 | | - implicit_extension_class = is_implicit_extension_class(cdef) |
| 137 | + implicit_extension_class, reason = is_implicit_extension_class(cdef) |
138 | 138 |
|
139 | 139 | # Classes with native_class=True should be extension classes, but they might |
140 | 140 | # not be able to be due to other reasons. Print an error in that case. |
141 | 141 | if explicit_native_class is True and not implicit_extension_class: |
142 | 142 | 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, |
144 | 146 | ) |
145 | 147 |
|
146 | 148 | return implicit_extension_class |
@@ -177,28 +179,37 @@ def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool |
177 | 179 | return None |
178 | 180 |
|
179 | 181 |
|
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 | + |
181 | 185 | for d in cdef.decorators: |
182 | | - # Classes that have any decorator other than supported decorators, are not extension classes |
183 | 186 | if ( |
184 | 187 | not is_trait_decorator(d) |
185 | 188 | and not is_dataclass_decorator(d) |
186 | 189 | and not get_mypyc_attr_call(d) |
187 | 190 | and not is_final_decorator(d) |
188 | 191 | ): |
189 | | - return False |
| 192 | + return ( |
| 193 | + False, |
| 194 | + "Classes that have decorators other than supported decorators" |
| 195 | + " can't be native classes.", |
| 196 | + ) |
190 | 197 |
|
191 | 198 | if cdef.info.typeddict_type: |
192 | | - return False |
| 199 | + return False, "TypedDict classes can't be native classes." |
193 | 200 | if cdef.info.is_named_tuple: |
194 | | - return False |
| 201 | + return False, "NamedTuple classes can't be native classes." |
195 | 202 | if cdef.info.metaclass_type and cdef.info.metaclass_type.type.fullname not in ( |
196 | 203 | "abc.ABCMeta", |
197 | 204 | "typing.TypingMeta", |
198 | 205 | "typing.GenericMeta", |
199 | 206 | ): |
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, "" |
202 | 213 |
|
203 | 214 |
|
204 | 215 | def get_func_def(op: FuncDef | Decorator | OverloadedFuncDef) -> FuncDef: |
|
0 commit comments