Esempio n. 1
0
def prepare_non_ext_class_def(path: str, module_name: str, cdef: ClassDef,
                              errors: Errors, mapper: Mapper) -> None:

    ir = mapper.type_to_ir[cdef.info]
    info = cdef.info

    for name, node in info.names.items():
        if isinstance(node.node, (FuncDef, Decorator)):
            prepare_method_def(ir, module_name, cdef, mapper, node.node)
        elif isinstance(node.node, OverloadedFuncDef):
            # Handle case for property with both a getter and a setter
            if node.node.is_property:
                if not is_valid_multipart_property_def(node.node):
                    errors.error("Unsupported property decorator semantics",
                                 path, cdef.line)
                for item in node.node.items:
                    prepare_method_def(ir, module_name, cdef, mapper, item)
            # Handle case for regular function overload
            else:
                prepare_method_def(ir, module_name, cdef, mapper,
                                   get_func_def(node.node))

    if any(cls in mapper.type_to_ir and mapper.type_to_ir[cls].is_ext_class
           for cls in info.mro):
        errors.error(
            "Non-extension classes may not inherit from extension classes",
            path, cdef.line)
Esempio n. 2
0
def get_module_func_defs(module: MypyFile) -> Iterable[FuncDef]:
    """Collect all of the (non-method) functions declared in a module."""
    for name, node in module.names.items():
        # We need to filter out functions that are imported or
        # aliases.  The best way to do this seems to be by
        # checking that the fullname matches.
        if (isinstance(node.node, (FuncDef, Decorator, OverloadedFuncDef))
                and node.fullname == module.fullname + '.' + name):
            yield get_func_def(node.node)
Esempio n. 3
0
    def visit_class_def(self, cdef: ClassDef) -> None:
        ir = self.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)):
            self.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:
                    self.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 = self.allocate_class(cdef)  # type: Optional[Value]
            non_ext = None  # type: Optional[NonExtClassInfo]
            dataclass_non_ext = self.dataclass_non_ext_info(cdef)
        else:
            non_ext_bases = self.populate_non_ext_bases(cdef)
            non_ext_metaclass = self.find_non_ext_metaclass(cdef, non_ext_bases)
            non_ext_dict = self.setup_non_ext_dict(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.primitive_op(new_dict_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[Lvalue]

        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
                    self.error("Property setters not supported in non-extension classes",
                               stmt.line)
                for item in stmt.items:
                    with self.builder.catch_errors(stmt.line):
                        BuildFuncIR(self.builder).visit_method(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 self.builder.catch_errors(stmt.line):
                    BuildFuncIR(self.builder).visit_method(cdef, non_ext, get_func_def(stmt))
            elif isinstance(stmt, PassStmt):
                continue
            elif isinstance(stmt, AssignmentStmt):
                if len(stmt.lvalues) != 1:
                    self.error("Multiple assignment in class bodies not supported", stmt.line)
                    continue
                lvalue = stmt.lvalues[0]
                if not isinstance(lvalue, NameExpr):
                    self.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:
                    self.add_non_ext_class_attr(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 = self.builder.load_native_type_object(cdef.fullname)
                value = self.accept(stmt.rvalue)
                self.primitive_op(
                    py_setattr_op, [typ, self.load_static_unicode(lvalue.name), value], stmt.line)
                if self.builder.non_function_scope() and stmt.is_final_def:
                    self.builder.init_final_static(lvalue, value, cdef.name)
            elif isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr):
                # Docstring. Ignore
                pass
            else:
                self.error("Unsupported statement in class body", stmt.line)

        if not non_ext:  # That is, an extension class
            self.generate_attr_defaults(cdef)
            self.create_ne_from_eq(cdef)
            if dataclass_non_ext:
                assert type_obj
                self.dataclass_finalize(cdef, dataclass_non_ext, type_obj)
        else:
            # Dynamically create the class via the type constructor
            non_ext_class = self.load_non_ext_class(ir, non_ext, cdef.line)
            non_ext_class = self.load_decorated_class(cdef, non_ext_class)

            # Save the decorated class
            self.add(InitStatic(non_ext_class, cdef.name, self.module_name, NAMESPACE_TYPE))

            # Add the non-extension class to the dict
            self.primitive_op(dict_set_item_op,
                              [
                                  self.builder.load_globals_dict(),
                                  self.load_static_unicode(cdef.name),
                                  non_ext_class
                              ], cdef.line)

            # Cache any cachable class attributes
            self.cache_class_attrs(attrs_to_cache, cdef)

            # Set this attribute back to None until the next non-extension class is visited.
            self.non_ext_info = None