def create_non_ext_info(self) -> NonExtClassInfo: non_ext_bases = populate_non_ext_bases(self.builder, self.cdef) non_ext_metaclass = find_non_ext_metaclass(self.builder, self.cdef, non_ext_bases) non_ext_dict = setup_non_ext_dict(self.builder, self.cdef, non_ext_metaclass, non_ext_bases) # We populate __annotations__ for non-extension classes # because dataclasses uses it to determine which attributes to compute on. # TODO: Maybe generate more precise types for annotations non_ext_anns = self.builder.call_c(dict_new_op, [], self.cdef.line) return NonExtClassInfo(non_ext_dict, non_ext_bases, non_ext_anns, non_ext_metaclass)
def create_non_ext_info(self) -> NonExtClassInfo: """Set up a NonExtClassInfo to track dataclass attributes. In addition to setting up a normal extension class for dataclasses, we also collect its class attributes like a non-extension class so that we can hand them to the dataclass decorator. """ return NonExtClassInfo( self.builder.call_c(dict_new_op, [], self.cdef.line), self.builder.add(TupleSet([], self.cdef.line)), self.builder.call_c(dict_new_op, [], self.cdef.line), self.builder.add(LoadAddress(type_object_op.type, type_object_op.src, self.cdef.line)) )
def dataclass_non_ext_info(builder: IRBuilder, cdef: ClassDef) -> Optional[NonExtClassInfo]: """Set up a NonExtClassInfo to track dataclass attributes. In addition to setting up a normal extension class for dataclasses, we also collect its class attributes like a non-extension class so that we can hand them to the dataclass decorator. """ if is_dataclass(cdef): return NonExtClassInfo( builder.call_c(dict_new_op, [], cdef.line), builder.add(TupleSet([], cdef.line)), builder.call_c(dict_new_op, [], cdef.line), builder.add(LoadAddress(type_object_op.type, type_object_op.src, cdef.line)) ) else: return None
def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: """Create IR for a class definition. This can generate both extension (native) and non-extension classes. These are generated in very different ways. In the latter case we construct a Python type object at runtime by doing the equivalent of "type(name, bases, dict)" in IR. Extension classes are defined via C structs that are generated later in mypyc.codegen.emitclass. This is the main entry point to this module. """ ir = builder.mapper.type_to_ir[cdef.info] # We do this check here because the base field of parent # classes aren't necessarily populated yet at # prepare_class_def time. if any(ir.base_mro[i].base != ir. base_mro[i + 1] for i in range(len(ir.base_mro) - 1)): builder.error("Non-trait MRO must be linear", cdef.line) if ir.allow_interpreted_subclasses: for parent in ir.mro: if not parent.allow_interpreted_subclasses: builder.error( 'Base class "{}" does not allow interpreted subclasses'.format( parent.fullname), cdef.line) # Currently, we only create non-extension classes for classes that are # decorated or inherit from Enum. Classes decorated with @trait do not # apply here, and are handled in a different way. if ir.is_ext_class: # If the class is not decorated, generate an extension class for it. type_obj = allocate_class(builder, cdef) # type: Optional[Value] non_ext = None # type: Optional[NonExtClassInfo] dataclass_non_ext = dataclass_non_ext_info(builder, cdef) else: non_ext_bases = populate_non_ext_bases(builder, cdef) non_ext_metaclass = find_non_ext_metaclass(builder, cdef, non_ext_bases) non_ext_dict = setup_non_ext_dict(builder, cdef, non_ext_metaclass, non_ext_bases) # We populate __annotations__ for non-extension classes # because dataclasses uses it to determine which attributes to compute on. # TODO: Maybe generate more precise types for annotations non_ext_anns = builder.call_c(dict_new_op, [], cdef.line) non_ext = NonExtClassInfo(non_ext_dict, non_ext_bases, non_ext_anns, non_ext_metaclass) dataclass_non_ext = None type_obj = None attrs_to_cache = [] # type: List[Tuple[Lvalue, RType]] for stmt in cdef.defs.body: if isinstance(stmt, OverloadedFuncDef) and stmt.is_property: if not ir.is_ext_class: # properties with both getters and setters in non_extension # classes not supported builder.error("Property setters not supported in non-extension classes", stmt.line) for item in stmt.items: with builder.catch_errors(stmt.line): transform_method(builder, cdef, non_ext, get_func_def(item)) elif isinstance(stmt, (FuncDef, Decorator, OverloadedFuncDef)): # Ignore plugin generated methods (since they have no # bodies to compile and will need to have the bodies # provided by some other mechanism.) if cdef.info.names[stmt.name].plugin_generated: continue with builder.catch_errors(stmt.line): transform_method(builder, cdef, non_ext, get_func_def(stmt)) elif isinstance(stmt, PassStmt): continue elif isinstance(stmt, AssignmentStmt): if len(stmt.lvalues) != 1: builder.error("Multiple assignment in class bodies not supported", stmt.line) continue lvalue = stmt.lvalues[0] if not isinstance(lvalue, NameExpr): builder.error("Only assignment to variables is supported in class bodies", stmt.line) continue # We want to collect class variables in a dictionary for both real # non-extension classes and fake dataclass ones. var_non_ext = non_ext or dataclass_non_ext if var_non_ext: add_non_ext_class_attr(builder, var_non_ext, lvalue, stmt, cdef, attrs_to_cache) if non_ext: continue # Variable declaration with no body if isinstance(stmt.rvalue, TempNode): continue # Only treat marked class variables as class variables. if not (is_class_var(lvalue) or stmt.is_final_def): continue typ = builder.load_native_type_object(cdef.fullname) value = builder.accept(stmt.rvalue) builder.call_c( py_setattr_op, [typ, builder.load_str(lvalue.name), value], stmt.line) if builder.non_function_scope() and stmt.is_final_def: builder.init_final_static(lvalue, value, cdef.name) elif isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr): # Docstring. Ignore pass else: builder.error("Unsupported statement in class body", stmt.line) if not non_ext: # That is, an extension class generate_attr_defaults(builder, cdef) create_ne_from_eq(builder, cdef) if dataclass_non_ext: assert type_obj dataclass_finalize(builder, cdef, dataclass_non_ext, type_obj) else: # Dynamically create the class via the type constructor non_ext_class = load_non_ext_class(builder, ir, non_ext, cdef.line) non_ext_class = load_decorated_class(builder, cdef, non_ext_class) # Save the decorated class builder.add(InitStatic(non_ext_class, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add the non-extension class to the dict builder.call_c(dict_set_item_op, [ builder.load_globals_dict(), builder.load_str(cdef.name), non_ext_class ], cdef.line) # Cache any cachable class attributes cache_class_attrs(builder, attrs_to_cache, cdef)