Beispiel #1
0
    def test_is_proper_subtype_and_subtype_literal_types(self) -> None:
        fx = self.fx

        lit1 = LiteralType(1, fx.a)
        lit2 = LiteralType("foo", fx.d)
        lit3 = LiteralType("bar", fx.d)

        assert_true(is_proper_subtype(lit1, fx.a))
        assert_false(is_proper_subtype(lit1, fx.d))
        assert_false(is_proper_subtype(fx.a, lit1))
        assert_true(is_proper_subtype(fx.uninhabited, lit1))
        assert_false(is_proper_subtype(lit1, fx.uninhabited))
        assert_true(is_proper_subtype(lit1, lit1))
        assert_false(is_proper_subtype(lit1, lit2))
        assert_false(is_proper_subtype(lit2, lit3))

        assert_true(is_subtype(lit1, fx.a))
        assert_false(is_subtype(lit1, fx.d))
        assert_false(is_subtype(fx.a, lit1))
        assert_true(is_subtype(fx.uninhabited, lit1))
        assert_false(is_subtype(lit1, fx.uninhabited))
        assert_true(is_subtype(lit1, lit1))
        assert_false(is_subtype(lit1, lit2))
        assert_false(is_subtype(lit2, lit3))

        assert_false(is_proper_subtype(lit1, fx.anyt))
        assert_false(is_proper_subtype(fx.anyt, lit1))

        assert_true(is_subtype(lit1, fx.anyt))
        assert_true(is_subtype(fx.anyt, lit1))
Beispiel #2
0
    def test_literal_type(self) -> None:
        a = self.fx.a
        d = self.fx.d
        lit1 = LiteralType(1, a)
        lit2 = LiteralType(2, a)
        lit3 = LiteralType("foo", d)

        self.assert_join(lit1, lit1, lit1)
        self.assert_join(lit1, a, a)
        self.assert_join(lit1, d, self.fx.o)
        self.assert_join(lit1, lit2, a)
        self.assert_join(lit1, lit3, self.fx.o)
        self.assert_join(lit1, self.fx.anyt, self.fx.anyt)
        self.assert_join(UnionType([lit1, lit2]), lit2, UnionType([lit1,
                                                                   lit2]))
        self.assert_join(UnionType([lit1, lit2]), a, a)
        self.assert_join(UnionType([lit1, lit3]), a, UnionType([a, lit3]))
        self.assert_join(UnionType([d, lit3]), lit3, d)
        self.assert_join(UnionType([d, lit3]), d, UnionType([d, lit3]))
        self.assert_join(UnionType([a, lit1]), lit1, a)
        self.assert_join(UnionType([a, lit1]), lit2, a)
        self.assert_join(UnionType([lit1, lit2]), UnionType([lit1, lit2]),
                         UnionType([lit1, lit2]))

        # The order in which we try joining two unions influences the
        # ordering of the items in the final produced unions. So, we
        # manually call 'assert_simple_join' and tune the output
        # after swapping the arguments here.
        self.assert_simple_join(UnionType([lit1, lit2]),
                                UnionType([lit2, lit3]),
                                UnionType([lit1, lit2, lit3]))
        self.assert_simple_join(UnionType([lit2, lit3]),
                                UnionType([lit1, lit2]),
                                UnionType([lit2, lit3, lit1]))
Beispiel #3
0
def int_neg_callback(ctx: MethodContext) -> Type:
    """Infer a more precise return type for int.__neg__.

    This is mainly used to infer the return type as LiteralType
    if the original underlying object is a LiteralType object
    """
    if isinstance(ctx.type,
                  Instance) and ctx.type.last_known_value is not None:
        value = ctx.type.last_known_value.value
        fallback = ctx.type.last_known_value.fallback
        if isinstance(value, int):
            if is_literal_type_like(ctx.api.type_context[-1]):
                return LiteralType(value=-value, fallback=fallback)
            else:
                return ctx.type.copy_modified(last_known_value=LiteralType(
                    value=-value,
                    fallback=ctx.type,
                    line=ctx.type.line,
                    column=ctx.type.column,
                ))
    elif isinstance(ctx.type, LiteralType):
        value = ctx.type.value
        fallback = ctx.type.fallback
        if isinstance(value, int):
            return LiteralType(value=-value, fallback=fallback)
    return ctx.default_return_type
Beispiel #4
0
def try_expanding_sum_type_to_union(typ: Type,
                                    target_fullname: str) -> ProperType:
    """Attempts to recursively expand any enum Instances with the given target_fullname
    into a Union of all of its component LiteralTypes.

    For example, if we have:

        class Color(Enum):
            RED = 1
            BLUE = 2
            YELLOW = 3

        class Status(Enum):
            SUCCESS = 1
            FAILURE = 2
            UNKNOWN = 3

    ...and if we call `try_expanding_enum_to_union(Union[Color, Status], 'module.Color')`,
    this function will return Literal[Color.RED, Color.BLUE, Color.YELLOW, Status].
    """
    typ = get_proper_type(typ)

    if isinstance(typ, UnionType):
        items = [
            try_expanding_sum_type_to_union(item, target_fullname)
            for item in typ.items
        ]
        return make_simplified_union(items, contract_literals=False)
    elif isinstance(typ, Instance) and typ.type.fullname == target_fullname:
        if typ.type.is_enum:
            new_items = []
            for name, symbol in typ.type.names.items():
                if not isinstance(symbol.node, Var):
                    continue
                # Skip "_order_" and "__order__", since Enum will remove it
                if name in ("_order_", "__order__"):
                    continue
                new_items.append(LiteralType(name, typ))
            # SymbolTables are really just dicts, and dicts are guaranteed to preserve
            # insertion order only starting with Python 3.7. So, we sort these for older
            # versions of Python to help make tests deterministic.
            #
            # We could probably skip the sort for Python 3.6 since people probably run mypy
            # only using CPython, but we might as well for the sake of full correctness.
            if sys.version_info < (3, 7):
                new_items.sort(key=lambda lit: lit.value)
            return make_simplified_union(new_items, contract_literals=False)
        elif typ.type.fullname == "builtins.bool":
            return make_simplified_union(
                [LiteralType(True, typ),
                 LiteralType(False, typ)],
                contract_literals=False)

    return typ
Beispiel #5
0
def analyze_is_method(ctx: AttributeContext) -> Type:
    """Callback to analyze the ``is_*`` methods on ``CGEnum``s.
    """
    typ = ctx.type
    assert isinstance(ctx.context, MemberExpr)
    name = ctx.context.name
    enum_value = name[len('is_'):]

    assert isinstance(
        typ,
        Instance,
    ), f'Got strange type: {typ} ({type(typ)})'

    bool_type = ctx.api.named_generic_type('builtins.bool', [])
    last_known_value = typ.last_known_value
    if isinstance(last_known_value, LiteralType):
        is_value = last_known_value.value == enum_value
        enum_type = last_known_value.fallback.type
        if not is_value and not _enum_has(enum_type, enum_value):
            ctx.api.fail(
                (f'The enum {enum_type.name} has no attribute "{name}" as'
                 f' the enum has no member "{enum_value}"'), ctx.context)
        literal_type = LiteralType(is_value, fallback=bool_type)
        return bool_type.copy_modified(last_known_value=literal_type)

    enum_type = typ.type
    if not _enum_has(enum_type, enum_value):
        ctx.api.fail(
            (f'The enum {enum_type.name} has no attribute "{name}" as the enum'
             f' has no member "{enum_value}"'), ctx.context)

    return bool_type
Beispiel #6
0
    def test_literal_type(self) -> None:
        a = self.fx.a
        d = self.fx.d
        lit1 = LiteralType(1, a)
        lit2 = LiteralType(2, a)
        lit3 = LiteralType("foo", d)

        self.assert_meet(lit1, lit1, lit1)
        self.assert_meet(lit1, a, lit1)
        self.assert_meet_uninhabited(lit1, lit3)
        self.assert_meet_uninhabited(lit1, lit2)
        self.assert_meet(UnionType([lit1, lit2]), lit1, lit1)
        self.assert_meet(UnionType([lit1, lit2]), UnionType([lit2, lit3]), lit2)
        self.assert_meet(UnionType([lit1, lit2]), UnionType([lit1, lit2]), UnionType([lit1, lit2]))
        self.assert_meet(lit1, self.fx.anyt, lit1)
        self.assert_meet(lit1, self.fx.o, lit1)
Beispiel #7
0
    def analyze_unbound_type_without_type_info(self, t: UnboundType, sym: SymbolTableNode,
                                               defining_literal: bool) -> Type:
        """Figure out what an unbound type that doesn't refer to a TypeInfo node means.

        This is something unusual. We try our best to find out what it is.
        """
        name = sym.fullname
        if name is None:
            assert sym.node is not None
            name = sym.node.name()
        # Option 1:
        # Something with an Any type -- make it an alias for Any in a type
        # context. This is slightly problematic as it allows using the type 'Any'
        # as a base class -- however, this will fail soon at runtime so the problem
        # is pretty minor.
        if isinstance(sym.node, Var) and isinstance(sym.node.type, AnyType):
            return AnyType(TypeOfAny.from_unimported_type,
                           missing_import_name=sym.node.type.missing_import_name)
        # Option 2:
        # Unbound type variable. Currently these may be still valid,
        # for example when defining a generic type alias.
        unbound_tvar = (isinstance(sym.node, TypeVarExpr) and
                        self.tvar_scope.get_binding(sym) is None)
        if self.allow_unbound_tvars and unbound_tvar:
            return t

        # Option 3:
        # Enum value. Note: we only want to return a LiteralType when
        # we're using this enum value specifically within context of
        # a "Literal[...]" type. So, if `defining_literal` is not set,
        # we bail out early with an error.
        #
        # If, in the distant future, we decide to permit things like
        # `def foo(x: Color.RED) -> None: ...`, we can remove that
        # check entirely.
        if isinstance(sym.node, Var) and sym.node.info and sym.node.info.is_enum:
            value = sym.node.name()
            base_enum_short_name = sym.node.info.name()
            if not defining_literal:
                msg = message_registry.INVALID_TYPE_RAW_ENUM_VALUE.format(
                    base_enum_short_name, value)
                self.fail(msg, t)
                return AnyType(TypeOfAny.from_error)
            return LiteralType(
                value=value,
                fallback=Instance(sym.node.info, [], line=t.line, column=t.column),
                line=t.line,
                column=t.column,
            )

        # None of the above options worked. We parse the args (if there are any)
        # to make sure there are no remaining semanal-only types, then give up.
        t = t.copy_modified(args=self.anal_array(t.args))
        self.fail('Invalid type "{}"'.format(name), t)

        # TODO: Would it be better to always return Any instead of UnboundType
        # in case of an error? On one hand, UnboundType has a name so error messages
        # are more detailed, on the other hand, some of them may be bogus,
        # see https://github.com/python/mypy/issues/4987.
        return t
Beispiel #8
0
def type_add(ctx, *args):
    trecv = ctx.type
    targs = ctx.arg_types
    if trecv.__class__ is LiteralType and len(targs) == 1 and targs[0][0].__class__ is LiteralType:
        ret = trecv.value + targs[0][0].value
        return LiteralType(ret, fallback=ctx.api.named_generic_type('builtins.int', []))
    else:
        return None
Beispiel #9
0
    def test_literal_type(self) -> None:
        b = self.fx.b  # Reminder: b is a subclass of a
        d = self.fx.d

        lit1 = LiteralType(1, b)
        lit2 = LiteralType(2, b)
        lit3 = LiteralType("foo", d)

        self.assert_same(lit1, lit1)
        self.assert_same(UnionType([lit1, lit2]), UnionType([lit1, lit2]))
        self.assert_same(UnionType([lit1, lit2]), UnionType([lit2, lit1]))
        self.assert_not_same(lit1, b)
        self.assert_not_same(lit1, lit2)
        self.assert_not_same(lit1, lit3)

        self.assert_not_same(lit1, self.fx.anyt)
        self.assert_not_same(lit1, self.fx.nonet)
Beispiel #10
0
 def visit_literal_type(self, t: LiteralType) -> Type:
     fallback = t.fallback.accept(self)
     assert isinstance(fallback, Instance)
     return LiteralType(
         value=t.value,
         fallback=fallback,
         line=t.line,
         column=t.column,
     )
Beispiel #11
0
    def test_literal_type(self) -> None:
        a = self.fx.a
        d = self.fx.d
        lit1 = LiteralType(1, a)
        lit2 = LiteralType(2, a)
        lit3 = LiteralType("foo", d)

        self.assert_meet(lit1, lit1, lit1)
        self.assert_meet(lit1, a, lit1)
        self.assert_meet_uninhabited(lit1, lit3)
        self.assert_meet_uninhabited(lit1, lit2)
        self.assert_meet(UnionType([lit1, lit2]), lit1, lit1)
        self.assert_meet(UnionType([lit1, lit2]), UnionType([lit2, lit3]), lit2)
        self.assert_meet(UnionType([lit1, lit2]), UnionType([lit1, lit2]), UnionType([lit1, lit2]))
        self.assert_meet(lit1, self.fx.anyt, lit1)
        self.assert_meet(lit1, self.fx.o, lit1)

        assert_true(is_same_type(lit1, narrow_declared_type(lit1, a)))
        assert_true(is_same_type(lit2, narrow_declared_type(lit2, a)))
Beispiel #12
0
def mutate_typeclass_def(
    typeclass: Instance,
    definition_fullname: str,
    ctx: Union[FunctionContext, MethodContext],
) -> None:
    """Adds definition fullname to the typeclass type."""
    str_fallback = ctx.api.str_type()  # type: ignore

    typeclass.args = (
        *typeclass.args[:3],
        LiteralType(definition_fullname, str_fallback),
    )
Beispiel #13
0
    def test_literal_type(self) -> None:
        a = self.fx.a
        b = self.fx.b  # Reminder: b is a subclass of a
        d = self.fx.d

        # Literals are not allowed to contain floats, but we're going to
        # test them anyways, just to make sure the semantics are robust
        # against these kinds of things.
        lit0 = LiteralType(cast(int, 1.0), a)
        lit1 = LiteralType(1, b)
        lit2 = LiteralType(2, b)
        lit3 = LiteralType("foo", d)

        self.assert_same(lit1, lit1)
        self.assert_same(UnionType([lit1, lit2]), UnionType([lit1, lit2]))
        self.assert_same(UnionType([lit1, lit2]), UnionType([lit2, lit1]))
        self.assert_not_same(lit1, b)
        self.assert_not_same(lit0, lit1)
        self.assert_not_same(lit1, lit2)
        self.assert_not_same(lit1, lit3)

        self.assert_not_same(lit1, self.fx.anyt)
        self.assert_not_same(lit1, self.fx.nonet)
Beispiel #14
0
def analyze_none_member_access(name: str, typ: NoneType, mx: MemberContext) -> Type:
    is_python_3 = mx.chk.options.python_version[0] >= 3
    # In Python 2 "None" has exactly the same attributes as "object". Python 3 adds a single
    # extra attribute, "__bool__".
    if is_python_3 and name == '__bool__':
        literal_false = LiteralType(False, fallback=mx.named_type('builtins.bool'))
        return CallableType(arg_types=[],
                            arg_kinds=[],
                            arg_names=[],
                            ret_type=literal_false,
                            fallback=mx.named_type('builtins.function'))
    elif mx.chk.should_suppress_optional_error([typ]):
        return AnyType(TypeOfAny.from_error)
    else:
        return _analyze_member_access(name, mx.named_type('builtins.object'), mx)
Beispiel #15
0
def analyze_enum_class_attribute_access(
    itype: Instance,
    name: str,
    mx: MemberContext,
) -> Optional[Type]:
    # Skip these since Enum will remove it
    if name in ENUM_REMOVED_PROPS:
        return mx.msg.has_no_attr(mx.original_type, itype, name, mx.context,
                                  mx.module_symbol_table)
    # For other names surrendered by underscores, we don't make them Enum members
    if name.startswith('__') and name.endswith("__") and name.replace(
            '_', '') != '':
        return None

    enum_literal = LiteralType(name, fallback=itype)
    return itype.copy_modified(last_known_value=enum_literal)
Beispiel #16
0
def coerce_to_literal(typ: Type) -> ProperType:
    """Recursively converts any Instances that have a last_known_value or are
    instances of enum types with a single value into the corresponding LiteralType.
    """
    typ = get_proper_type(typ)
    if isinstance(typ, UnionType):
        new_items = [coerce_to_literal(item) for item in typ.items]
        return make_simplified_union(new_items)
    elif isinstance(typ, Instance):
        if typ.last_known_value:
            return typ.last_known_value
        elif typ.type.is_enum:
            enum_values = get_enum_values(typ)
            if len(enum_values) == 1:
                return LiteralType(value=enum_values[0], fallback=typ)
    return typ
Beispiel #17
0
def newfield_hook(ctx: FunctionContext) -> Type:
    if not isinstance(ctx.default_return_type, Instance):
        return ctx.default_return_type
    if ctx.arg_names and ["required"] in ctx.arg_names:
        req_type = ctx.arg_types[ctx.arg_names.index(["required"])][0]
        if (not isinstance(req_type, Instance)
                or not isinstance(req_type.last_known_value, LiteralType)
                or not isinstance(req_type.last_known_value.value, bool)):
            ctx.api.fail("Can't decipher whether field is required or not",
                         ctx.context)
            return ctx.default_return_type
        required = req_type.last_known_value
    else:
        bool_type = ctx.api.named_type("bool")  # type: ignore
        required = LiteralType(False, bool_type)
    return ctx.default_return_type.copy_modified(args=[required])
Beispiel #18
0
def _add_match_args(ctx: 'mypy.plugin.ClassDefContext',
                    attributes: List[Attribute]) -> None:
    if ('__match_args__' not in ctx.cls.info.names
            or ctx.cls.info.names['__match_args__'].plugin_generated):
        str_type = ctx.api.named_type('builtins.str')
        match_args = TupleType(
            [
                str_type.copy_modified(last_known_value=LiteralType(
                    attr.name, fallback=str_type), )
                for attr in attributes if not attr.kw_only and attr.init
            ],
            fallback=ctx.api.named_type('builtins.tuple'),
        )
        add_attribute_to_class(
            api=ctx.api,
            cls=ctx.cls,
            name='__match_args__',
            typ=match_args,
        )
Beispiel #19
0
def analyze_enum_class_attribute_access(itype: Instance,
                                        name: str,
                                        mx: MemberContext,
                                        ) -> Optional[Type]:
    # Skip "_order_" and "__order__", since Enum will remove it
    if name in ("_order_", "__order__"):
        return mx.msg.has_no_attr(
            mx.original_type, itype, name, mx.context, mx.module_symbol_table
        )
    # For other names surrendered by underscores, we don't make them Enum members
    if name.startswith('__') and name.endswith("__") and name.replace('_', '') != '':
        return None

    enum_literal = LiteralType(name, fallback=itype)
    # When we analyze enums, the corresponding Instance is always considered to be erased
    # due to how the signature of Enum.__new__ is `(cls: Type[_T], value: object) -> _T`
    # in typeshed. However, this is really more of an implementation detail of how Enums
    # are typed, and we really don't want to treat every single Enum value as if it were
    # from type variable substitution. So we reset the 'erased' field here.
    return itype.copy_modified(erased=False, last_known_value=enum_literal)
Beispiel #20
0
def enum_name_callback(ctx: 'mypy.plugin.AttributeContext') -> Type:
    """This plugin refines the 'name' attribute in enums to act as if
    they were declared to be final.

    For example, the expression 'MyEnum.FOO.name' normally is inferred
    to be of type 'str'.

    This plugin will instead make the inferred type be a 'str' where the
    last known value is 'Literal["FOO"]'. This means it would be legal to
    use 'MyEnum.FOO.name' in contexts that expect a Literal type, just like
    any other Final variable or attribute.

    This plugin assumes that the provided context is an attribute access
    matching one of the strings found in 'ENUM_NAME_ACCESS'.
    """
    enum_field_name = _extract_underlying_field_name(ctx.type)
    if enum_field_name is None:
        return ctx.default_attr_type
    else:
        str_type = ctx.api.named_generic_type('builtins.str', [])
        literal_type = LiteralType(enum_field_name, fallback=str_type)
        return str_type.copy_modified(last_known_value=literal_type)
Beispiel #21
0
    def __init__(self, variance: int = COVARIANT) -> None:
        # The 'object' class
        self.oi = self.make_type_info('builtins.object')  # class object
        self.o = Instance(self.oi, [])  # object

        # Type variables (these are effectively global)

        def make_type_var(name: str, id: int, values: List[Type],
                          upper_bound: Type, variance: int) -> TypeVarType:
            return TypeVarType(name, name, id, values, upper_bound, variance)

        self.t = make_type_var('T', 1, [], self.o,
                               variance)  # T`1 (type variable)
        self.tf = make_type_var('T', -1, [], self.o,
                                variance)  # T`-1 (type variable)
        self.tf2 = make_type_var('T', -2, [], self.o,
                                 variance)  # T`-2 (type variable)
        self.s = make_type_var('S', 2, [], self.o,
                               variance)  # S`2 (type variable)
        self.s1 = make_type_var('S', 1, [], self.o,
                                variance)  # S`1 (type variable)
        self.sf = make_type_var('S', -2, [], self.o,
                                variance)  # S`-2 (type variable)
        self.sf1 = make_type_var('S', -1, [], self.o,
                                 variance)  # S`-1 (type variable)

        # Simple types
        self.anyt = AnyType(TypeOfAny.special_form)
        self.nonet = NoneType()
        self.uninhabited = UninhabitedType()

        # Abstract class TypeInfos

        # class F
        self.fi = self.make_type_info('F', is_abstract=True)

        # class F2
        self.f2i = self.make_type_info('F2', is_abstract=True)

        # class F3(F)
        self.f3i = self.make_type_info('F3', is_abstract=True, mro=[self.fi])

        # Class TypeInfos
        self.std_tuplei = self.make_type_info('builtins.tuple',
                                              mro=[self.oi],
                                              typevars=['T'],
                                              variances=[COVARIANT
                                                         ])  # class tuple
        self.type_typei = self.make_type_info('builtins.type')  # class type
        self.bool_type_info = self.make_type_info('builtins.bool')
        self.functioni = self.make_type_info(
            'builtins.function')  # function TODO
        self.ai = self.make_type_info('A', mro=[self.oi])  # class A
        self.bi = self.make_type_info('B', mro=[self.ai,
                                                self.oi])  # class B(A)
        self.ci = self.make_type_info('C', mro=[self.ai,
                                                self.oi])  # class C(A)
        self.di = self.make_type_info('D', mro=[self.oi])  # class D
        # class E(F)
        self.ei = self.make_type_info('E', mro=[self.fi, self.oi])
        # class E2(F2, F)
        self.e2i = self.make_type_info('E2', mro=[self.f2i, self.fi, self.oi])
        # class E3(F, F2)
        self.e3i = self.make_type_info('E3', mro=[self.fi, self.f2i, self.oi])

        # Generic class TypeInfos
        # G[T]
        self.gi = self.make_type_info('G',
                                      mro=[self.oi],
                                      typevars=['T'],
                                      variances=[variance])
        # G2[T]
        self.g2i = self.make_type_info('G2',
                                       mro=[self.oi],
                                       typevars=['T'],
                                       variances=[variance])
        # H[S, T]
        self.hi = self.make_type_info('H',
                                      mro=[self.oi],
                                      typevars=['S', 'T'],
                                      variances=[variance, variance])
        # GS[T, S] <: G[S]
        self.gsi = self.make_type_info('GS',
                                       mro=[self.gi, self.oi],
                                       typevars=['T', 'S'],
                                       variances=[variance, variance],
                                       bases=[Instance(self.gi, [self.s])])
        # GS2[S] <: G[S]
        self.gs2i = self.make_type_info('GS2',
                                        mro=[self.gi, self.oi],
                                        typevars=['S'],
                                        variances=[variance],
                                        bases=[Instance(self.gi, [self.s1])])
        # list[T]
        self.std_listi = self.make_type_info('builtins.list',
                                             mro=[self.oi],
                                             typevars=['T'],
                                             variances=[variance])

        # Instance types
        self.std_tuple = Instance(self.std_tuplei, [self.anyt])  # tuple
        self.type_type = Instance(self.type_typei, [])  # type
        self.function = Instance(self.functioni, [])  # function TODO
        self.a = Instance(self.ai, [])  # A
        self.b = Instance(self.bi, [])  # B
        self.c = Instance(self.ci, [])  # C
        self.d = Instance(self.di, [])  # D

        self.e = Instance(self.ei, [])  # E
        self.e2 = Instance(self.e2i, [])  # E2
        self.e3 = Instance(self.e3i, [])  # E3

        self.f = Instance(self.fi, [])  # F
        self.f2 = Instance(self.f2i, [])  # F2
        self.f3 = Instance(self.f3i, [])  # F3

        # Generic instance types
        self.ga = Instance(self.gi, [self.a])  # G[A]
        self.gb = Instance(self.gi, [self.b])  # G[B]
        self.gd = Instance(self.gi, [self.d])  # G[D]
        self.go = Instance(self.gi, [self.o])  # G[object]
        self.gt = Instance(self.gi, [self.t])  # G[T`1]
        self.gtf = Instance(self.gi, [self.tf])  # G[T`-1]
        self.gtf2 = Instance(self.gi, [self.tf2])  # G[T`-2]
        self.gs = Instance(self.gi, [self.s])  # G[S]
        self.gdyn = Instance(self.gi, [self.anyt])  # G[Any]
        self.gn = Instance(self.gi, [NoneType()])  # G[None]

        self.g2a = Instance(self.g2i, [self.a])  # G2[A]

        self.gsaa = Instance(self.gsi, [self.a, self.a])  # GS[A, A]
        self.gsab = Instance(self.gsi, [self.a, self.b])  # GS[A, B]
        self.gsba = Instance(self.gsi, [self.b, self.a])  # GS[B, A]

        self.gs2a = Instance(self.gs2i, [self.a])  # GS2[A]
        self.gs2b = Instance(self.gs2i, [self.b])  # GS2[B]
        self.gs2d = Instance(self.gs2i, [self.d])  # GS2[D]

        self.hab = Instance(self.hi, [self.a, self.b])  # H[A, B]
        self.haa = Instance(self.hi, [self.a, self.a])  # H[A, A]
        self.hbb = Instance(self.hi, [self.b, self.b])  # H[B, B]
        self.hts = Instance(self.hi, [self.t, self.s])  # H[T, S]
        self.had = Instance(self.hi, [self.a, self.d])  # H[A, D]
        self.hao = Instance(self.hi, [self.a, self.o])  # H[A, object]

        self.lsta = Instance(self.std_listi, [self.a])  # List[A]
        self.lstb = Instance(self.std_listi, [self.b])  # List[B]

        self.lit1 = LiteralType(1, self.a)
        self.lit2 = LiteralType(2, self.a)
        self.lit3 = LiteralType("foo", self.d)
        self.lit4 = LiteralType(4, self.a)
        self.lit1_inst = Instance(self.ai, [], last_known_value=self.lit1)
        self.lit2_inst = Instance(self.ai, [], last_known_value=self.lit2)
        self.lit3_inst = Instance(self.di, [], last_known_value=self.lit3)
        self.lit4_inst = Instance(self.ai, [], last_known_value=self.lit4)

        self.type_a = TypeType.make_normalized(self.a)
        self.type_b = TypeType.make_normalized(self.b)
        self.type_c = TypeType.make_normalized(self.c)
        self.type_d = TypeType.make_normalized(self.d)
        self.type_t = TypeType.make_normalized(self.t)
        self.type_any = TypeType.make_normalized(self.anyt)

        self._add_bool_dunder(self.bool_type_info)
        self._add_bool_dunder(self.ai)
Beispiel #22
0
def analyze_class_attribute_access(
        itype: Instance,
        name: str,
        mx: MemberContext,
        override_info: Optional[TypeInfo] = None,
        original_vars: Optional[List[TypeVarDef]] = None) -> Optional[Type]:
    """Analyze access to an attribute on a class object.

    itype is the return type of the class object callable, original_type is the type
    of E in the expression E.var, original_vars are type variables of the class callable
    (for generic classes).
    """
    info = itype.type
    if override_info:
        info = override_info

    node = info.get(name)
    if not node:
        if info.fallback_to_any:
            return AnyType(TypeOfAny.special_form)
        return None

    is_decorated = isinstance(node.node, Decorator)
    is_method = is_decorated or isinstance(node.node, FuncBase)
    if mx.is_lvalue:
        if is_method:
            mx.msg.cant_assign_to_method(mx.context)
        if isinstance(node.node, TypeInfo):
            mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, mx.context)

    # If a final attribute was declared on `self` in `__init__`, then it
    # can't be accessed on the class object.
    if node.implicit and isinstance(node.node, Var) and node.node.is_final:
        mx.msg.fail(
            message_registry.CANNOT_ACCESS_FINAL_INSTANCE_ATTR.format(
                node.node.name), mx.context)

    # An assignment to final attribute on class object is also always an error,
    # independently of types.
    if mx.is_lvalue and not mx.chk.get_final_context():
        check_final_member(name, info, mx.msg, mx.context)

    if info.is_enum and not (mx.is_lvalue or is_decorated or is_method):
        enum_literal = LiteralType(name, fallback=itype)
        # When we analyze enums, the corresponding Instance is always considered to be erased
        # due to how the signature of Enum.__new__ is `(cls: Type[_T], value: object) -> _T`
        # in typeshed. However, this is really more of an implementation detail of how Enums
        # are typed, and we really don't want to treat every single Enum value as if it were
        # from type variable substitution. So we reset the 'erased' field here.
        return itype.copy_modified(erased=False, last_known_value=enum_literal)

    t = node.type
    if t:
        if isinstance(t, PartialType):
            symnode = node.node
            assert isinstance(symnode, Var)
            return mx.chk.handle_partial_var_type(t, mx.is_lvalue, symnode,
                                                  mx.context)

        # Find the class where method/variable was defined.
        if isinstance(node.node, Decorator):
            super_info = node.node.var.info  # type: Optional[TypeInfo]
        elif isinstance(node.node, (Var, SYMBOL_FUNCBASE_TYPES)):
            super_info = node.node.info
        else:
            super_info = None

        # Map the type to how it would look as a defining class. For example:
        #     class C(Generic[T]): ...
        #     class D(C[Tuple[T, S]]): ...
        #     D[int, str].method()
        # Here itype is D[int, str], isuper is C[Tuple[int, str]].
        if not super_info:
            isuper = None
        else:
            isuper = map_instance_to_supertype(itype, super_info)

        if isinstance(node.node, Var):
            assert isuper is not None
            # Check if original variable type has type variables. For example:
            #     class C(Generic[T]):
            #         x: T
            #     C.x  # Error, ambiguous access
            #     C[int].x  # Also an error, since C[int] is same as C at runtime
            if isinstance(t, TypeVarType) or has_type_vars(t):
                # Exception: access on Type[...], including first argument of class methods is OK.
                if not isinstance(get_proper_type(mx.original_type),
                                  TypeType) or node.implicit:
                    if node.node.is_classvar:
                        message = message_registry.GENERIC_CLASS_VAR_ACCESS
                    else:
                        message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
                    mx.msg.fail(message, mx.context)

            # Erase non-mapped variables, but keep mapped ones, even if there is an error.
            # In the above example this means that we infer following types:
            #     C.x -> Any
            #     C[int].x -> int
            t = erase_typevars(expand_type_by_instance(t, isuper))

        is_classmethod = (
            (is_decorated and cast(Decorator, node.node).func.is_class)
            or (isinstance(node.node, FuncBase) and node.node.is_class))
        t = get_proper_type(t)
        if isinstance(t, FunctionLike) and is_classmethod:
            t = check_self_arg(t, mx.self_type, False, mx.context, name,
                               mx.msg)
        result = add_class_tvars(t,
                                 isuper,
                                 is_classmethod,
                                 mx.self_type,
                                 original_vars=original_vars)
        if not mx.is_lvalue:
            result = analyze_descriptor_access(mx.original_type,
                                               result,
                                               mx.builtin_type,
                                               mx.msg,
                                               mx.context,
                                               chk=mx.chk)
        return result
    elif isinstance(node.node, Var):
        mx.not_ready_callback(name, mx.context)
        return AnyType(TypeOfAny.special_form)

    if isinstance(node.node, TypeVarExpr):
        mx.msg.fail(
            message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(
                info.name, name), mx.context)
        return AnyType(TypeOfAny.from_error)

    if isinstance(node.node, TypeInfo):
        return type_object_type(node.node, mx.builtin_type)

    if isinstance(node.node, MypyFile):
        # Reference to a module object.
        return mx.builtin_type('types.ModuleType')

    if (isinstance(node.node, TypeAlias)
            and isinstance(get_proper_type(node.node.target), Instance)):
        return instance_alias_type(node.node, mx.builtin_type)

    if is_decorated:
        assert isinstance(node.node, Decorator)
        if node.node.type:
            return node.node.type
        else:
            mx.not_ready_callback(name, mx.context)
            return AnyType(TypeOfAny.from_error)
    else:
        assert isinstance(node.node, FuncBase)
        typ = function_type(node.node, mx.builtin_type('builtins.function'))
        # Note: if we are accessing class method on class object, the cls argument is bound.
        # Annotated and/or explicit class methods go through other code paths above, for
        # unannotated implicit class methods we do this here.
        if node.node.is_class:
            typ = bind_self(typ, is_classmethod=True)
        return typ
Beispiel #23
0
 def const(self, ctx: AnalyzeTypeContext, const: Union[int, str, bool],
           **kwargs) -> LiteralType:
     """Generate a ``Literal`` for a const value."""
     name = type(const).__name__
     return LiteralType(const, named_builtin_type(ctx, name, []))
Beispiel #24
0
    def transform(self) -> None:
        """Apply all the necessary transformations to the underlying
        dataclass so as to ensure it is fully type checked according
        to the rules in PEP 557.
        """
        ctx = self._ctx
        info = self._ctx.cls.info
        attributes = self.collect_attributes()
        if attributes is None:
            # Some definitions are not ready, defer() should be already called.
            return
        for attr in attributes:
            if attr.type is None:
                ctx.api.defer()
                return
        decorator_arguments = {
            'init':
            _get_decorator_bool_argument(self._ctx, 'init', True),
            'eq':
            _get_decorator_bool_argument(self._ctx, 'eq', True),
            'order':
            _get_decorator_bool_argument(self._ctx, 'order', False),
            'frozen':
            _get_decorator_bool_argument(self._ctx, 'frozen', False),
            'slots':
            _get_decorator_bool_argument(self._ctx, 'slots', False),
            'match_args':
            _get_decorator_bool_argument(self._ctx, 'match_args', True),
        }
        py_version = self._ctx.api.options.python_version

        # If there are no attributes, it may be that the semantic analyzer has not
        # processed them yet. In order to work around this, we can simply skip generating
        # __init__ if there are no attributes, because if the user truly did not define any,
        # then the object default __init__ with an empty signature will be present anyway.
        if (decorator_arguments['init']
                and ('__init__' not in info.names
                     or info.names['__init__'].plugin_generated)
                and attributes):

            args = [
                attr.to_argument() for attr in attributes
                if attr.is_in_init and not self._is_kw_only_type(attr.type)
            ]

            if info.fallback_to_any:
                # Make positional args optional since we don't know their order.
                # This will at least allow us to typecheck them if they are called
                # as kwargs
                for arg in args:
                    if arg.kind == ARG_POS:
                        arg.kind = ARG_OPT

                nameless_var = Var('')
                args = [
                    Argument(nameless_var, AnyType(TypeOfAny.explicit), None,
                             ARG_STAR),
                    *args,
                    Argument(nameless_var, AnyType(TypeOfAny.explicit), None,
                             ARG_STAR2),
                ]

            add_method(
                ctx,
                '__init__',
                args=args,
                return_type=NoneType(),
            )

        if (decorator_arguments['eq'] and info.get('__eq__') is None
                or decorator_arguments['order']):
            # Type variable for self types in generated methods.
            obj_type = ctx.api.named_type('builtins.object')
            self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME,
                                         info.fullname + '.' + SELF_TVAR_NAME,
                                         [], obj_type)
            info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)

        # Add <, >, <=, >=, but only if the class has an eq method.
        if decorator_arguments['order']:
            if not decorator_arguments['eq']:
                ctx.api.fail('eq must be True if order is True', ctx.cls)

            for method_name in ['__lt__', '__gt__', '__le__', '__ge__']:
                # Like for __eq__ and __ne__, we want "other" to match
                # the self type.
                obj_type = ctx.api.named_type('builtins.object')
                order_tvar_def = TypeVarType(
                    SELF_TVAR_NAME, info.fullname + '.' + SELF_TVAR_NAME, -1,
                    [], obj_type)
                order_return_type = ctx.api.named_type('builtins.bool')
                order_args = [
                    Argument(Var('other', order_tvar_def), order_tvar_def,
                             None, ARG_POS)
                ]

                existing_method = info.get(method_name)
                if existing_method is not None and not existing_method.plugin_generated:
                    assert existing_method.node
                    ctx.api.fail(
                        'You may not have a custom %s method when order=True' %
                        method_name,
                        existing_method.node,
                    )

                add_method(
                    ctx,
                    method_name,
                    args=order_args,
                    return_type=order_return_type,
                    self_type=order_tvar_def,
                    tvar_def=order_tvar_def,
                )

        if decorator_arguments['frozen']:
            self._freeze(attributes)
        else:
            self._propertize_callables(attributes)

        if decorator_arguments['slots']:
            self.add_slots(info,
                           attributes,
                           correct_version=py_version >= (3, 10))

        self.reset_init_only_vars(info, attributes)

        if (decorator_arguments['match_args']
                and ('__match_args__' not in info.names
                     or info.names['__match_args__'].plugin_generated)
                and attributes):
            str_type = ctx.api.named_type("builtins.str")
            literals: List[Type] = [
                LiteralType(attr.name, str_type) for attr in attributes
                if attr.is_in_init
            ]
            match_args_type = TupleType(literals,
                                        ctx.api.named_type("builtins.tuple"))
            add_attribute_to_class(ctx.api,
                                   ctx.cls,
                                   "__match_args__",
                                   match_args_type,
                                   final=True)

        self._add_dataclass_fields_magic_attribute()

        info.metadata['dataclass'] = {
            'attributes': [attr.serialize() for attr in attributes],
            'frozen': decorator_arguments['frozen'],
        }
Beispiel #25
0
 def visit_literal_type(self, t: LiteralType) -> Type:
     if self.check_recursion(t):
         return AnyType(TypeOfAny.from_error)
     fallback = self.visit_instance(t.fallback, from_fallback=True)
     assert isinstance(fallback, Instance)
     return LiteralType(t.value, fallback, t.line, t.column)
Beispiel #26
0
def translate_kind_instance(typ: Type) -> Type:  # noqa: WPS, C901
    """
    We use this ugly hack to translate ``KindN[x, y]`` into ``x[y]``.

    This is required due to the fact that ``KindN``
    can be nested in other types, like: ``List[KindN[...]]``.

    We will refactor this code after ``TypeTranslator``
    is released in ``[email protected]`` version.
    """
    typ = get_proper_type(typ)

    if isinstance(typ, _LEAF_TYPES):  # noqa: WPS223
        return typ
    elif isinstance(typ, Instance):
        last_known_value: Optional[LiteralType] = None
        if typ.last_known_value is not None:
            raw_last_known_value = translate_kind_instance(
                typ.last_known_value)
            assert isinstance(raw_last_known_value, LiteralType)
            last_known_value = raw_last_known_value
        instance = Instance(
            typ=typ.type,
            args=_translate_types(typ.args),
            line=typ.line,
            column=typ.column,
            last_known_value=last_known_value,
        )
        if typ.type.fullname == TYPED_KINDN:  # That's where we do the change
            return _process_kinded_type(instance)
        return instance

    elif isinstance(typ, CallableType):
        return typ.copy_modified(
            arg_types=_translate_types(typ.arg_types),
            ret_type=translate_kind_instance(typ.ret_type),
        )
    elif isinstance(typ, TupleType):
        return TupleType(
            _translate_types(typ.items),
            translate_kind_instance(typ.partial_fallback),  # type: ignore
            typ.line,
            typ.column,
        )
    elif isinstance(typ, TypedDictType):
        dict_items = {
            item_name: translate_kind_instance(item_type)
            for item_name, item_type in typ.items.items()
        }
        return TypedDictType(
            dict_items,
            typ.required_keys,
            translate_kind_instance(typ.fallback),  # type: ignore
            typ.line,
            typ.column,
        )
    elif isinstance(typ, LiteralType):
        fallback = translate_kind_instance(typ.fallback)
        assert isinstance(fallback, Instance)
        return LiteralType(
            value=typ.value,
            fallback=fallback,
            line=typ.line,
            column=typ.column,
        )
    elif isinstance(typ, UnionType):
        return UnionType(_translate_types(typ.items), typ.line, typ.column)
    elif isinstance(typ, Overloaded):
        functions: List[CallableType] = []
        for func in typ.items():
            new = translate_kind_instance(func)
            assert isinstance(new, CallableType)
            functions.append(new)
        return Overloaded(items=functions)
    elif isinstance(typ, TypeType):
        return TypeType.make_normalized(
            translate_kind_instance(typ.item),
            line=typ.line,
            column=typ.column,
        )
    return typ
Beispiel #27
0
def schema_callback(ctx: FunctionContext) -> Type:
    # todo assert ctx.args = [[StrExpr]]
    fname = ctx.args[0][0].value
    return ctx.default_return_type.copy_modified(
        args=[LiteralType(fname, ctx.arg_types[0][0])])
Beispiel #28
0
def analyze_class_attribute_access(itype: Instance,
                                   name: str,
                                   mx: MemberContext,
                                   override_info: Optional[TypeInfo] = None) -> Optional[Type]:
    """original_type is the type of E in the expression E.var"""
    info = itype.type
    if override_info:
        info = override_info

    node = info.get(name)
    if not node:
        if info.fallback_to_any:
            return AnyType(TypeOfAny.special_form)
        return None

    is_decorated = isinstance(node.node, Decorator)
    is_method = is_decorated or isinstance(node.node, FuncBase)
    if mx.is_lvalue:
        if is_method:
            mx.msg.cant_assign_to_method(mx.context)
        if isinstance(node.node, TypeInfo):
            mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, mx.context)

    # If a final attribute was declared on `self` in `__init__`, then it
    # can't be accessed on the class object.
    if node.implicit and isinstance(node.node, Var) and node.node.is_final:
        mx.msg.fail(message_registry.CANNOT_ACCESS_FINAL_INSTANCE_ATTR
                    .format(node.node.name()), mx.context)

    # An assignment to final attribute on class object is also always an error,
    # independently of types.
    if mx.is_lvalue and not mx.chk.get_final_context():
        check_final_member(name, info, mx.msg, mx.context)

    if info.is_enum and not (mx.is_lvalue or is_decorated or is_method):
        enum_literal = LiteralType(name, fallback=itype)
        return itype.copy_modified(last_known_value=enum_literal)

    t = node.type
    if t:
        if isinstance(t, PartialType):
            symnode = node.node
            assert isinstance(symnode, Var)
            return mx.chk.handle_partial_var_type(t, mx.is_lvalue, symnode, mx.context)

        # Find the class where method/variable was defined.
        if isinstance(node.node, Decorator):
            super_info = node.node.var.info  # type: Optional[TypeInfo]
        elif isinstance(node.node, (Var, SYMBOL_FUNCBASE_TYPES)):
            super_info = node.node.info
        else:
            super_info = None

        # Map the type to how it would look as a defining class. For example:
        #     class C(Generic[T]): ...
        #     class D(C[Tuple[T, S]]): ...
        #     D[int, str].method()
        # Here itype is D[int, str], isuper is C[Tuple[int, str]].
        if not super_info:
            isuper = None
        else:
            isuper = map_instance_to_supertype(itype, super_info)

        if isinstance(node.node, Var):
            assert isuper is not None
            # Check if original variable type has type variables. For example:
            #     class C(Generic[T]):
            #         x: T
            #     C.x  # Error, ambiguous access
            #     C[int].x  # Also an error, since C[int] is same as C at runtime
            if isinstance(t, TypeVarType) or get_type_vars(t):
                # Exception: access on Type[...], including first argument of class methods is OK.
                if not isinstance(get_proper_type(mx.original_type), TypeType):
                    mx.msg.fail(message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS, mx.context)

            # Erase non-mapped variables, but keep mapped ones, even if there is an error.
            # In the above example this means that we infer following types:
            #     C.x -> Any
            #     C[int].x -> int
            t = erase_typevars(expand_type_by_instance(t, isuper))

        is_classmethod = ((is_decorated and cast(Decorator, node.node).func.is_class)
                          or (isinstance(node.node, FuncBase) and node.node.is_class))
        result = add_class_tvars(get_proper_type(t), itype, isuper, is_classmethod,
                                 mx.builtin_type, mx.original_type)
        if not mx.is_lvalue:
            result = analyze_descriptor_access(mx.original_type, result, mx.builtin_type,
                                               mx.msg, mx.context, chk=mx.chk)
        return result
    elif isinstance(node.node, Var):
        mx.not_ready_callback(name, mx.context)
        return AnyType(TypeOfAny.special_form)

    if isinstance(node.node, TypeVarExpr):
        mx.msg.fail(message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(
                    info.name(), name), mx.context)
        return AnyType(TypeOfAny.from_error)

    if isinstance(node.node, TypeInfo):
        return type_object_type(node.node, mx.builtin_type)

    if isinstance(node.node, MypyFile):
        # Reference to a module object.
        return mx.builtin_type('types.ModuleType')

    if (isinstance(node.node, TypeAlias) and
            isinstance(get_proper_type(node.node.target), Instance)):
        return instance_alias_type(node.node, mx.builtin_type)

    if is_decorated:
        assert isinstance(node.node, Decorator)
        if node.node.type:
            return node.node.type
        else:
            mx.not_ready_callback(name, mx.context)
            return AnyType(TypeOfAny.from_error)
    else:
        return function_type(cast(FuncBase, node.node), mx.builtin_type('builtins.function'))
Beispiel #29
0
    def build_namedtuple_typeinfo(self, name: str, items: List[str],
                                  types: List[Type],
                                  default_items: Mapping[str, Expression],
                                  line: int) -> TypeInfo:
        strtype = self.api.named_type('builtins.str')
        implicit_any = AnyType(TypeOfAny.special_form)
        basetuple_type = self.api.named_type('builtins.tuple', [implicit_any])
        dictype = (self.api.named_type_or_none('builtins.dict',
                                               [strtype, implicit_any])
                   or self.api.named_type('builtins.object'))
        # Actual signature should return OrderedDict[str, Union[types]]
        ordereddictype = (self.api.named_type_or_none('builtins.dict',
                                                      [strtype, implicit_any])
                          or self.api.named_type('builtins.object'))
        fallback = self.api.named_type('builtins.tuple', [implicit_any])
        # Note: actual signature should accept an invariant version of Iterable[UnionType[types]].
        # but it can't be expressed. 'new' and 'len' should be callable types.
        iterable_type = self.api.named_type_or_none('typing.Iterable',
                                                    [implicit_any])
        function_type = self.api.named_type('builtins.function')

        literals: List[Type] = [LiteralType(item, strtype) for item in items]
        match_args_type = TupleType(literals, basetuple_type)

        info = self.api.basic_new_typeinfo(name, fallback, line)
        info.is_named_tuple = True
        tuple_base = TupleType(types, fallback)
        info.tuple_type = tuple_base
        info.line = line
        # For use by mypyc.
        info.metadata['namedtuple'] = {'fields': items.copy()}

        # We can't calculate the complete fallback type until after semantic
        # analysis, since otherwise base classes might be incomplete. Postpone a
        # callback function that patches the fallback.
        self.api.schedule_patch(PRIORITY_FALLBACKS,
                                lambda: calculate_tuple_fallback(tuple_base))

        def add_field(var: Var,
                      is_initialized_in_class: bool = False,
                      is_property: bool = False) -> None:
            var.info = info
            var.is_initialized_in_class = is_initialized_in_class
            var.is_property = is_property
            var._fullname = '%s.%s' % (info.fullname, var.name)
            info.names[var.name] = SymbolTableNode(MDEF, var)

        fields = [Var(item, typ) for item, typ in zip(items, types)]
        for var in fields:
            add_field(var, is_property=True)
        # We can't share Vars between fields and method arguments, since they
        # have different full names (the latter are normally used as local variables
        # in functions, so their full names are set to short names when generated methods
        # are analyzed).
        vars = [Var(item, typ) for item, typ in zip(items, types)]

        tuple_of_strings = TupleType([strtype for _ in items], basetuple_type)
        add_field(Var('_fields', tuple_of_strings),
                  is_initialized_in_class=True)
        add_field(Var('_field_types', dictype), is_initialized_in_class=True)
        add_field(Var('_field_defaults', dictype),
                  is_initialized_in_class=True)
        add_field(Var('_source', strtype), is_initialized_in_class=True)
        add_field(Var('__annotations__', ordereddictype),
                  is_initialized_in_class=True)
        add_field(Var('__doc__', strtype), is_initialized_in_class=True)
        add_field(Var('__match_args__', match_args_type),
                  is_initialized_in_class=True)

        tvd = TypeVarType(SELF_TVAR_NAME, info.fullname + '.' + SELF_TVAR_NAME,
                          -1, [], info.tuple_type)
        selftype = tvd

        def add_method(
            funcname: str,
            ret: Type,
            args: List[Argument],
            is_classmethod: bool = False,
            is_new: bool = False,
        ) -> None:
            if is_classmethod or is_new:
                first = [
                    Argument(Var('_cls'), TypeType.make_normalized(selftype),
                             None, ARG_POS)
                ]
            else:
                first = [Argument(Var('_self'), selftype, None, ARG_POS)]
            args = first + args

            types = [arg.type_annotation for arg in args]
            items = [arg.variable.name for arg in args]
            arg_kinds = [arg.kind for arg in args]
            assert None not in types
            signature = CallableType(cast(List[Type], types), arg_kinds, items,
                                     ret, function_type)
            signature.variables = [tvd]
            func = FuncDef(funcname, args, Block([]))
            func.info = info
            func.is_class = is_classmethod
            func.type = set_callable_name(signature, func)
            func._fullname = info.fullname + '.' + funcname
            func.line = line
            if is_classmethod:
                v = Var(funcname, func.type)
                v.is_classmethod = True
                v.info = info
                v._fullname = func._fullname
                func.is_decorated = True
                dec = Decorator(func, [NameExpr('classmethod')], v)
                dec.line = line
                sym = SymbolTableNode(MDEF, dec)
            else:
                sym = SymbolTableNode(MDEF, func)
            sym.plugin_generated = True
            info.names[funcname] = sym

        add_method('_replace',
                   ret=selftype,
                   args=[
                       Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT)
                       for var in vars
                   ])

        def make_init_arg(var: Var) -> Argument:
            default = default_items.get(var.name, None)
            kind = ARG_POS if default is None else ARG_OPT
            return Argument(var, var.type, default, kind)

        add_method('__new__',
                   ret=selftype,
                   args=[make_init_arg(var) for var in vars],
                   is_new=True)
        add_method('_asdict', args=[], ret=ordereddictype)
        special_form_any = AnyType(TypeOfAny.special_form)
        add_method('_make',
                   ret=selftype,
                   is_classmethod=True,
                   args=[
                       Argument(Var('iterable', iterable_type), iterable_type,
                                None, ARG_POS),
                       Argument(Var('new'), special_form_any, EllipsisExpr(),
                                ARG_NAMED_OPT),
                       Argument(Var('len'), special_form_any, EllipsisExpr(),
                                ARG_NAMED_OPT)
                   ])

        self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME,
                                     info.fullname + '.' + SELF_TVAR_NAME, [],
                                     info.tuple_type)
        info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)
        return info
Beispiel #30
0
    def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> Optional[List[Type]]:
        # This UnboundType was originally defined as a string.
        if isinstance(arg, UnboundType) and arg.original_str_expr is not None:
            assert arg.original_str_fallback is not None
            return [LiteralType(
                value=arg.original_str_expr,
                fallback=self.named_type_with_normalized_str(arg.original_str_fallback),
                line=arg.line,
                column=arg.column,
            )]

        # If arg is an UnboundType that was *not* originally defined as
        # a string, try expanding it in case it's a type alias or something.
        if isinstance(arg, UnboundType):
            self.nesting_level += 1
            try:
                arg = self.visit_unbound_type(arg, defining_literal=True)
            finally:
                self.nesting_level -= 1

        # Literal[...] cannot contain Any. Give up and add an error message
        # (if we haven't already).
        if isinstance(arg, AnyType):
            # Note: We can encounter Literals containing 'Any' under three circumstances:
            #
            # 1. If the user attempts use an explicit Any as a parameter
            # 2. If the user is trying to use an enum value imported from a module with
            #    no type hints, giving it an an implicit type of 'Any'
            # 3. If there's some other underlying problem with the parameter.
            #
            # We report an error in only the first two cases. In the third case, we assume
            # some other region of the code has already reported a more relevant error.
            #
            # TODO: Once we start adding support for enums, make sure we report a custom
            # error for case 2 as well.
            if arg.type_of_any not in (TypeOfAny.from_error, TypeOfAny.special_form):
                self.fail('Parameter {} of Literal[...] cannot be of type "Any"'.format(idx), ctx)
            return None
        elif isinstance(arg, RawExpressionType):
            # A raw literal. Convert it directly into a literal if we can.
            if arg.literal_value is None:
                name = arg.simple_name()
                if name in ('float', 'complex'):
                    msg = 'Parameter {} of Literal[...] cannot be of type "{}"'.format(idx, name)
                else:
                    msg = 'Invalid type: Literal[...] cannot contain arbitrary expressions'
                self.fail(msg, ctx)
                # Note: we deliberately ignore arg.note here: the extra info might normally be
                # helpful, but it generally won't make sense in the context of a Literal[...].
                return None

            # Remap bytes and unicode into the appropriate type for the correct Python version
            fallback = self.named_type_with_normalized_str(arg.base_type_name)
            assert isinstance(fallback, Instance)
            return [LiteralType(arg.literal_value, fallback, line=arg.line, column=arg.column)]
        elif isinstance(arg, (NoneType, LiteralType)):
            # Types that we can just add directly to the literal/potential union of literals.
            return [arg]
        elif isinstance(arg, Instance) and arg.last_known_value is not None:
            # Types generated from declarations like "var: Final = 4".
            return [arg.last_known_value]
        elif isinstance(arg, UnionType):
            out = []
            for union_arg in arg.items:
                union_result = self.analyze_literal_param(idx, union_arg, ctx)
                if union_result is None:
                    return None
                out.extend(union_result)
            return out
        elif isinstance(arg, ForwardRef):
            return [arg]
        else:
            self.fail('Parameter {} of Literal[...] is invalid'.format(idx), ctx)
            return None