|
29 | 29 | ) |
30 | 30 | from mypy.semanal import refers_to_fullname |
31 | 31 | from mypy.types import FINAL_DECORATOR_NAMES |
| 32 | +from mypyc.errors import Errors |
32 | 33 |
|
33 | 34 | DATACLASS_DECORATORS = {"dataclasses.dataclass", "attr.s", "attr.attrs"} |
34 | 35 |
|
@@ -125,15 +126,68 @@ def get_mypyc_attrs(stmt: ClassDef | Decorator) -> dict[str, Any]: |
125 | 126 | return attrs |
126 | 127 |
|
127 | 128 |
|
128 | | -def is_extension_class(cdef: ClassDef) -> bool: |
129 | | - if any( |
130 | | - not is_trait_decorator(d) |
131 | | - and not is_dataclass_decorator(d) |
132 | | - and not get_mypyc_attr_call(d) |
133 | | - and not is_final_decorator(d) |
134 | | - for d in cdef.decorators |
135 | | - ): |
| 129 | +def is_extension_class(path: str, cdef: ClassDef, errors: Errors) -> bool: |
| 130 | + # Check for @mypyc_attr(native_class=True/False) decorator. |
| 131 | + explicit_native_class = get_explicit_native_class(path, cdef, errors) |
| 132 | + |
| 133 | + # Classes with native_class=False are explicitly marked as non extension. |
| 134 | + if explicit_native_class is False: |
136 | 135 | return False |
| 136 | + |
| 137 | + implicit_extension_class = is_implicit_extension_class(cdef) |
| 138 | + |
| 139 | + # Classes with native_class=True should be extension classes, but they might |
| 140 | + # not be able to be due to other reasons. Print an error in that case. |
| 141 | + if explicit_native_class is True and not implicit_extension_class: |
| 142 | + errors.error( |
| 143 | + "Class is marked as native_class=True but it can't be a native class", path, cdef.line |
| 144 | + ) |
| 145 | + |
| 146 | + return implicit_extension_class |
| 147 | + |
| 148 | + |
| 149 | +def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool | None: |
| 150 | + """Return value of @mypyc_attr(native_class=True/False) decorator. |
| 151 | +
|
| 152 | + Look for a @mypyc_attr decorator with native_class=True/False and return |
| 153 | + the value assigned or None if it doesn't exist. Other values are an error. |
| 154 | + """ |
| 155 | + |
| 156 | + for d in cdef.decorators: |
| 157 | + mypyc_attr_call = get_mypyc_attr_call(d) |
| 158 | + if not mypyc_attr_call: |
| 159 | + continue |
| 160 | + |
| 161 | + for i, name in enumerate(mypyc_attr_call.arg_names): |
| 162 | + if name != "native_class": |
| 163 | + continue |
| 164 | + |
| 165 | + arg = mypyc_attr_call.args[i] |
| 166 | + if not isinstance(arg, NameExpr): |
| 167 | + errors.error("native_class must be used with True or False only", path, cdef.line) |
| 168 | + return None |
| 169 | + |
| 170 | + if arg.name == "False": |
| 171 | + return False |
| 172 | + elif arg.name == "True": |
| 173 | + return True |
| 174 | + else: |
| 175 | + errors.error("native_class must be used with True or False only", path, cdef.line) |
| 176 | + return None |
| 177 | + return None |
| 178 | + |
| 179 | + |
| 180 | +def is_implicit_extension_class(cdef: ClassDef) -> bool: |
| 181 | + for d in cdef.decorators: |
| 182 | + # Classes that have any decorator other than supported decorators, are not extension classes |
| 183 | + if ( |
| 184 | + not is_trait_decorator(d) |
| 185 | + and not is_dataclass_decorator(d) |
| 186 | + and not get_mypyc_attr_call(d) |
| 187 | + and not is_final_decorator(d) |
| 188 | + ): |
| 189 | + return False |
| 190 | + |
137 | 191 | if cdef.info.typeddict_type: |
138 | 192 | return False |
139 | 193 | if cdef.info.is_named_tuple: |
|
0 commit comments