Example #1
0
def _combine_protocols(p1: Instance, p2: Instance) -> Instance:
    def base_repr(base):
        if 'pfun.Intersection' in base.type.fullname:
            return ', '.join([repr(b) for b in base.type.bases])
        return repr(base)

    def get_bases(base):
        if 'pfun.Intersection' in base.type.fullname:
            bases = set()
            for b in base.type.bases:
                bases |= get_bases(b)
            return bases
        return set([base])

    names = p1.type.names.copy()
    names.update(p2.type.names)
    keywords = p1.type.defn.keywords.copy()
    keywords.update(p2.type.defn.keywords)
    bases = get_bases(p1) | get_bases(p2)
    bases_repr = ', '.join(sorted([repr(base) for base in bases]))
    name = f'Intersection[{bases_repr}]'
    defn = ClassDef(name, Block([]),
                    p1.type.defn.type_vars + p2.type.defn.type_vars,
                    [NameExpr(p1.type.fullname),
                     NameExpr(p2.type.fullname)], None, list(keywords.items()))
    defn.fullname = f'pfun.{name}'
    info = TypeInfo(names, defn, '')
    info.is_protocol = True
    info.is_abstract = True
    info.bases = [p1, p2]
    info.abstract_attributes = (p1.type.abstract_attributes +
                                p2.type.abstract_attributes)
    calculate_mro(info)
    return Instance(info, p1.args + p2.args)
Example #2
0
def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool,
                                    errors: Errors) -> None:
    """Calculate abstract status of a class.

    Set is_abstract of the type to True if the type has an unimplemented
    abstract attribute.  Also compute a list of abstract attributes.
    Report error is required ABCMeta metaclass is missing.
    """
    concrete = set()  # type: Set[str]
    abstract = []  # type: List[str]
    abstract_in_this_class = []  # type: List[str]
    for base in typ.mro:
        for name, symnode in base.names.items():
            node = symnode.node
            if isinstance(node, OverloadedFuncDef):
                # Unwrap an overloaded function definition. We can just
                # check arbitrarily the first overload item. If the
                # different items have a different abstract status, there
                # should be an error reported elsewhere.
                func = node.items[0]  # type: Optional[Node]
            else:
                func = node
            if isinstance(func, Decorator):
                fdef = func.func
                if fdef.is_abstract and name not in concrete:
                    typ.is_abstract = True
                    abstract.append(name)
                    if base is typ:
                        abstract_in_this_class.append(name)
            elif isinstance(node, Var):
                if node.is_abstract_var and name not in concrete:
                    typ.is_abstract = True
                    abstract.append(name)
                    if base is typ:
                        abstract_in_this_class.append(name)
            concrete.add(name)
    # In stubs, abstract classes need to be explicitly marked because it is too
    # easy to accidentally leave a concrete class abstract by forgetting to
    # implement some methods.
    typ.abstract_attributes = sorted(abstract)
    if is_stub_file:
        if typ.declared_metaclass and typ.declared_metaclass.type.fullname(
        ) == 'abc.ABCMeta':
            return
        if typ.is_protocol:
            return
        if abstract and not abstract_in_this_class:

            def report(message: str, severity: str) -> None:
                errors.report(typ.line, typ.column, message, severity=severity)

            attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract))
            report(
                "Class {} has abstract attributes {}".format(
                    typ.fullname(), attrs), 'error')
            report(
                "If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass",
                'note')
Example #3
0
 def strip_type_info(self, info: TypeInfo) -> None:
     info.type_vars = []
     info.bases = []
     info.abstract_attributes = []
     info.mro = []
     info.add_type_vars()
     info.tuple_type = None
     info.typeddict_type = None
     info.tuple_type = None
     info._cache = set()
     info._cache_proper = set()
Example #4
0
def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: Errors) -> None:
    """Calculate abstract status of a class.

    Set is_abstract of the type to True if the type has an unimplemented
    abstract attribute.  Also compute a list of abstract attributes.
    Report error is required ABCMeta metaclass is missing.
    """
    concrete = set()  # type: Set[str]
    abstract = []  # type: List[str]
    abstract_in_this_class = []  # type: List[str]
    for base in typ.mro:
        for name, symnode in base.names.items():
            node = symnode.node
            if isinstance(node, OverloadedFuncDef):
                # Unwrap an overloaded function definition. We can just
                # check arbitrarily the first overload item. If the
                # different items have a different abstract status, there
                # should be an error reported elsewhere.
                func = node.items[0]  # type: Optional[Node]
            else:
                func = node
            if isinstance(func, Decorator):
                fdef = func.func
                if fdef.is_abstract and name not in concrete:
                    typ.is_abstract = True
                    abstract.append(name)
                    if base is typ:
                        abstract_in_this_class.append(name)
            elif isinstance(node, Var):
                if node.is_abstract_var and name not in concrete:
                    typ.is_abstract = True
                    abstract.append(name)
                    if base is typ:
                        abstract_in_this_class.append(name)
            concrete.add(name)
    # In stubs, abstract classes need to be explicitly marked because it is too
    # easy to accidentally leave a concrete class abstract by forgetting to
    # implement some methods.
    typ.abstract_attributes = sorted(abstract)
    if is_stub_file:
        if typ.declared_metaclass and typ.declared_metaclass.type.fullname() == 'abc.ABCMeta':
            return
        if typ.is_protocol:
            return
        if abstract and not abstract_in_this_class:
            def report(message: str, severity: str) -> None:
                errors.report(typ.line, typ.column, message, severity=severity)

            attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract))
            report("Class {} has abstract attributes {}".format(typ.fullname(), attrs), 'error')
            report("If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass",
                   'note')
Example #5
0
 def strip_type_info(self, info: TypeInfo) -> None:
     info.type_vars = []
     info.bases = []
     info.is_abstract = False
     info.abstract_attributes = []
     info.mro = []
     info.add_type_vars()
     info.tuple_type = None
     info.typeddict_type = None
     info.tuple_type = None
     TypeState.reset_subtype_caches_for(info)
     info.declared_metaclass = None
     info.metaclass_type = None
Example #6
0
 def strip_type_info(self, info: TypeInfo) -> None:
     info.type_vars = []
     info.bases = []
     info.is_abstract = False
     info.abstract_attributes = []
     info.mro = []
     info.add_type_vars()
     info.tuple_type = None
     info.typeddict_type = None
     info.tuple_type = None
     info._cache = set()
     info._cache_proper = set()
     info.declared_metaclass = None
     info.metaclass_type = None
Example #7
0
    def strip_type_info(self, info: TypeInfo) -> List[SymbolNode]:
        info.type_vars = []
        info.bases = []
        info.is_abstract = False
        info.abstract_attributes = []
        info.mro = []
        info.add_type_vars()
        info.tuple_type = None
        info.typeddict_type = None
        info.tuple_type = None
        TypeState.reset_subtype_caches_for(info)
        info.declared_metaclass = None
        info.metaclass_type = None

        # We need to delete any entries that were generated by plugins,
        # since they will get regenerated.
        to_delete = [(k, v) for k, v in info.names.items() if v.plugin_generated]
        for k, _ in to_delete:
            del info.names[k]
        return [v.node for k, v in to_delete if v.node]
Example #8
0
    def strip_type_info(self, info: TypeInfo) -> List[SymbolNode]:
        info.type_vars = []
        info.bases = []
        info.is_abstract = False
        info.abstract_attributes = []
        info.mro = []
        info.add_type_vars()
        info.tuple_type = None
        info.typeddict_type = None
        info.tuple_type = None
        TypeState.reset_subtype_caches_for(info)
        info.declared_metaclass = None
        info.metaclass_type = None

        # We need to delete any entries that were generated by plugins,
        # since they will get regenerated.
        to_delete = [(k, v) for k, v in info.names.items()
                     if v.plugin_generated]
        for k, _ in to_delete:
            del info.names[k]
        return [v.node for k, v in to_delete if v.node]
Example #9
0
def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: Errors) -> None:
    """Calculate abstract status of a class.

    Set is_abstract of the type to True if the type has an unimplemented
    abstract attribute.  Also compute a list of abstract attributes.
    Report error is required ABCMeta metaclass is missing.
    """
    if typ.typeddict_type:
        return  # TypedDict can't be abstract
    concrete: Set[str] = set()
    abstract: List[str] = []
    abstract_in_this_class: List[str] = []
    if typ.is_newtype:
        # Special case: NewTypes are considered as always non-abstract, so they can be used as:
        #     Config = NewType('Config', Mapping[str, str])
        #     default = Config({'cannot': 'modify'})  # OK
        typ.abstract_attributes = []
        return
    for base in typ.mro:
        for name, symnode in base.names.items():
            node = symnode.node
            if isinstance(node, OverloadedFuncDef):
                # Unwrap an overloaded function definition. We can just
                # check arbitrarily the first overload item. If the
                # different items have a different abstract status, there
                # should be an error reported elsewhere.
                if node.items:  # can be empty for invalid overloads
                    func: Optional[Node] = node.items[0]
                else:
                    func = None
            else:
                func = node
            if isinstance(func, Decorator):
                fdef = func.func
                if fdef.is_abstract and name not in concrete:
                    typ.is_abstract = True
                    abstract.append(name)
                    if base is typ:
                        abstract_in_this_class.append(name)
            elif isinstance(node, Var):
                if node.is_abstract_var and name not in concrete:
                    typ.is_abstract = True
                    abstract.append(name)
                    if base is typ:
                        abstract_in_this_class.append(name)
            concrete.add(name)
    # In stubs, abstract classes need to be explicitly marked because it is too
    # easy to accidentally leave a concrete class abstract by forgetting to
    # implement some methods.
    typ.abstract_attributes = sorted(abstract)
    if is_stub_file:
        if typ.declared_metaclass and typ.declared_metaclass.type.fullname == 'abc.ABCMeta':
            return
        if typ.is_protocol:
            return
        if abstract and not abstract_in_this_class:
            def report(message: str, severity: str) -> None:
                errors.report(typ.line, typ.column, message, severity=severity)

            attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract))
            report("Class {} has abstract attributes {}".format(typ.fullname, attrs), 'error')
            report("If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass",
                   'note')
    if typ.is_final and abstract:
        attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract))
        errors.report(typ.line, typ.column,
                      "Final class {} has abstract attributes {}".format(typ.fullname, attrs))
Example #10
0
    def visit_instance(self, t: Instance) -> Type:
        if 'pfun.Intersection' == t.type.fullname:
            args = [get_proper_type(arg) for arg in t.args]
            if any(isinstance(arg, AnyType) for arg in args):
                return AnyType(TypeOfAny.special_form)
            if all(
                    hasattr(arg, 'type')
                    and arg.type.fullname == 'builtins.object'
                    for arg in args):
                return args[0]
            is_typevar = lambda arg: isinstance(arg, TypeVarType)
            has_type_attr = lambda arg: hasattr(arg, 'type')
            is_protocol = lambda arg: arg.type.is_protocol
            is_object = lambda arg: arg.type.fullname == 'builtins.object'
            if not all(
                    is_typevar(arg) or has_type_attr(arg) and
                (is_protocol(arg) or is_object(arg)) for arg in args):
                s = str(t)
                if self.inferred:
                    msg = (f'All arguments to "Intersection" '
                           f'must be protocols but inferred "{s}"')
                else:
                    msg = (f'All arguments to "Intersection" '
                           f'must be protocols, but got "{s}"')
                self.api.msg.fail(msg, self.context)
                return AnyType(TypeOfAny.special_form)
            if not has_no_typevars(t):
                return t
            bases = []
            for arg in args:
                if arg in bases:
                    continue
                bases.extend(self.get_bases(arg, []))
            if len(bases) == 1:
                return bases[0]
            bases_repr = ', '.join([repr(base) for base in bases])
            name = f'Intersection[{bases_repr}]'
            defn = ClassDef(name, Block([]), [], [
                NameExpr(arg.name) if isinstance(arg, TypeVarType) else
                NameExpr(arg.type.fullname) for arg in args
            ], None, [])
            defn.fullname = f'pfun.{name}'
            info = TypeInfo({}, defn, '')
            info.is_protocol = True
            info.is_abstract = True
            info.bases = bases
            attrs = []
            for base in bases:
                if isinstance(base, TypeVarType):
                    continue
                attrs.extend(base.type.abstract_attributes)
            info.abstract_attributes = attrs
            try:
                calculate_mro(info)
            except MroError:
                self.api.msg.fail(
                    'Cannot determine consistent method resolution '
                    'order (MRO) for "%s"' % defn.fullname, self.context)
                return AnyType(TypeOfAny.special_form)
            return Instance(info, [])

        return super().visit_instance(t)