示例#1
0
def analyze_type_alias(node: Node,
                       lookup_func: Callable[[str, Context], SymbolTableNode],
                       lookup_fqn_func: Callable[[str], SymbolTableNode],
                       fail_func: Callable[[str, Context], None]) -> Type:
    """Return type if node is valid as a type alias rvalue.

    Return None otherwise. 'node' must have been semantically analyzed.
    """
    # Quickly return None if the expression doesn't look like a type. Note
    # that we don't support straight string literals as type aliases
    # (only string literals within index expressions).
    if isinstance(node, RefExpr):
        if not (isinstance(node.node, TypeInfo) or
                node.fullname == 'typing.Any' or
                node.kind == TYPE_ALIAS):
            return None
    elif isinstance(node, IndexExpr):
        base = node.base
        if isinstance(base, RefExpr):
            if not (isinstance(base.node, TypeInfo) or
                    base.fullname in type_constructors):
                return None
        else:
            return None
    else:
        return None

    # It's a type alias (though it may be an invalid one).
    try:
        type = expr_to_unanalyzed_type(node)
    except TypeTranslationError:
        fail_func('Invalid type alias', node)
        return None
    analyzer = TypeAnalyser(lookup_func, lookup_fqn_func, fail_func)
    return type.accept(analyzer)
示例#2
0
    def check_newtype_args(self, name: str, call: CallExpr, context: Context) -> Optional[Type]:
        has_failed = False
        args, arg_kinds = call.args, call.arg_kinds
        if len(args) != 2 or arg_kinds[0] != ARG_POS or arg_kinds[1] != ARG_POS:
            self.fail("NewType(...) expects exactly two positional arguments", context)
            return None

        # Check first argument
        if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)):
            self.fail("Argument 1 to NewType(...) must be a string literal", context)
            has_failed = True
        elif args[0].value != name:
            msg = "String argument 1 '{}' to NewType(...) does not match variable name '{}'"
            self.fail(msg.format(args[0].value, name), context)
            has_failed = True

        # Check second argument
        msg = "Argument 2 to NewType(...) must be a valid type"
        try:
            unanalyzed_type = expr_to_unanalyzed_type(args[1])
        except TypeTranslationError:
            self.fail(msg, context)
            return None

        # We want to use our custom error message (see above), so we suppress
        # the default error message for invalid types here.
        old_type = self.api.anal_type(unanalyzed_type, report_invalid_types=False)

        # The caller of this function assumes that if we return a Type, it's always
        # a valid one. So, we translate AnyTypes created from errors into None.
        if isinstance(old_type, AnyType) and old_type.is_from_error:
            self.fail(msg, context)
            return None

        return None if has_failed else old_type
示例#3
0
    def parse_namedtuple_fields_with_types(self, nodes: List[Expression], context: Context
                                           ) -> Optional[Tuple[List[str], List[Type],
                                                               List[Expression],
                                                         bool]]:
        """Parse typed named tuple fields.

        Return (names, types, defaults, error ocurred), or None if at least one of
        the types is not ready.
        """
        items = []  # type: List[str]
        types = []  # type: List[Type]
        for item in nodes:
            if isinstance(item, TupleExpr):
                if len(item.items) != 2:
                    return self.fail_namedtuple_arg("Invalid NamedTuple field definition",
                                                    item)
                name, type_node = item.items
                if isinstance(name, (StrExpr, BytesExpr, UnicodeExpr)):
                    items.append(name.value)
                else:
                    return self.fail_namedtuple_arg("Invalid NamedTuple() field name", item)
                try:
                    type = expr_to_unanalyzed_type(type_node)
                except TypeTranslationError:
                    return self.fail_namedtuple_arg('Invalid field type', type_node)
                analyzed = self.api.anal_type(type)
                # These should be all known, otherwise we would defer in visit_assignment_stmt().
                if analyzed is None:
                    return None
                types.append(analyzed)
            else:
                return self.fail_namedtuple_arg("Tuple expected as NamedTuple() field", item)
        return items, types, [], True
示例#4
0
    def parse_typeddict_fields_with_types(
            self,
            dict_items: List[Tuple[Optional[Expression], Expression]],
            context: Context) -> Optional[Tuple[List[str], List[Type], bool]]:
        """Parse typed dict items passed as pairs (name expression, type expression).

        Return names, types, was there an error. If some type is not ready, return None.
        """
        items = []  # type: List[str]
        types = []  # type: List[Type]
        for (field_name_expr, field_type_expr) in dict_items:
            if isinstance(field_name_expr, (StrExpr, BytesExpr, UnicodeExpr)):
                items.append(field_name_expr.value)
            else:
                name_context = field_name_expr or field_type_expr
                self.fail_typeddict_arg("Invalid TypedDict() field name", name_context)
                return [], [], False
            try:
                type = expr_to_unanalyzed_type(field_type_expr)
            except TypeTranslationError:
                self.fail_typeddict_arg('Invalid field type', field_type_expr)
                return [], [], False
            analyzed = self.api.anal_type(type)
            if analyzed is None:
                return None
            types.append(analyzed)
        return items, types, True
示例#5
0
文件: attrs.py 项目: sixolet/mypy
def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
                                 auto_attribs: bool,
                                 lhs: NameExpr,
                                 rvalue: CallExpr,
                                 stmt: AssignmentStmt) -> Optional[Attribute]:
    """Return an Attribute from the assignment or None if you can't make one."""
    if auto_attribs and not stmt.new_syntax:
        # auto_attribs requires an annotation on *every* attr.ib.
        assert lhs.node is not None
        ctx.api.msg.need_annotation_for_var(lhs.node, stmt)
        return None

    if len(stmt.lvalues) > 1:
        ctx.api.fail("Too many names for one attribute", stmt)
        return None

    # This is the type that belongs in the __init__ method for this attrib.
    init_type = stmt.type

    # Read all the arguments from the call.
    init = _get_bool_argument(ctx, rvalue, 'init', True)
    # TODO: Check for attr.NOTHING
    attr_has_default = bool(_get_argument(rvalue, 'default'))

    # If the type isn't set through annotation but is passed through `type=` use that.
    type_arg = _get_argument(rvalue, 'type')
    if type_arg and not init_type:
        try:
            un_type = expr_to_unanalyzed_type(type_arg)
        except TypeTranslationError:
            ctx.api.fail('Invalid argument to type', type_arg)
        else:
            init_type = ctx.api.anal_type(un_type)
            if init_type and isinstance(lhs.node, Var) and not lhs.node.type:
                # If there is no annotation, add one.
                lhs.node.type = init_type
                lhs.is_inferred_def = False

    # Note: convert is deprecated but works the same as converter.
    converter = _get_argument(rvalue, 'converter')
    convert = _get_argument(rvalue, 'convert')
    if convert and converter:
        ctx.api.fail("Can't pass both `convert` and `converter`.", rvalue)
    elif convert:
        ctx.api.fail("convert is deprecated, use converter", rvalue)
        converter = convert
    converter_name = _get_converter_name(ctx, converter)

    return Attribute(lhs.name, ctx.cls.info, attr_has_default, init, converter_name, stmt)
示例#6
0
 def parse_typeddict_fields_with_types(
         self,
         dict_items: List[Tuple[Optional[Expression], Expression]],
         context: Context) -> Tuple[List[str], List[Type], bool]:
     items = []  # type: List[str]
     types = []  # type: List[Type]
     for (field_name_expr, field_type_expr) in dict_items:
         if isinstance(field_name_expr, (StrExpr, BytesExpr, UnicodeExpr)):
             items.append(field_name_expr.value)
         else:
             name_context = field_name_expr or field_type_expr
             self.fail_typeddict_arg("Invalid TypedDict() field name", name_context)
             return [], [], False
         try:
             type = expr_to_unanalyzed_type(field_type_expr)
         except TypeTranslationError:
             self.fail_typeddict_arg('Invalid field type', field_type_expr)
             return [], [], False
         types.append(self.api.anal_type(type))
     return items, types, True
示例#7
0
 def parse_namedtuple_fields_with_types(self, nodes: List[Expression], context: Context
                                        ) -> Tuple[List[str], List[Type], List[Expression],
                                                   bool]:
     items = []  # type: List[str]
     types = []  # type: List[Type]
     for item in nodes:
         if isinstance(item, TupleExpr):
             if len(item.items) != 2:
                 return self.fail_namedtuple_arg("Invalid NamedTuple field definition",
                                                 item)
             name, type_node = item.items
             if isinstance(name, (StrExpr, BytesExpr, UnicodeExpr)):
                 items.append(name.value)
             else:
                 return self.fail_namedtuple_arg("Invalid NamedTuple() field name", item)
             try:
                 type = expr_to_unanalyzed_type(type_node)
             except TypeTranslationError:
                 return self.fail_namedtuple_arg('Invalid field type', type_node)
             types.append(self.api.anal_type(type))
         else:
             return self.fail_namedtuple_arg("Tuple expected as NamedTuple() field", item)
     return items, types, [], True
示例#8
0
def analyze_type_alias(
        node: Expression,
        api: SemanticAnalyzerInterface,
        tvar_scope: TypeVarScope,
        plugin: Plugin,
        options: Options,
        is_typeshed_stub: bool,
        allow_unnormalized: bool = False,
        in_dynamic_func: bool = False,
        global_scope: bool = True,
        warn_bound_tvar: bool = False) -> Optional[Tuple[Type, Set[str]]]:
    """Analyze r.h.s. of a (potential) type alias definition.

    If `node` is valid as a type alias rvalue, return the resulting type and a set of
    full names of type aliases it depends on (directly or indirectly).
    Return None otherwise. 'node' must have been semantically analyzed.
    """
    # Quickly return None if the expression doesn't look like a type. Note
    # that we don't support straight string literals as type aliases
    # (only string literals within index expressions).
    if isinstance(node, RefExpr):
        # Note that this misses the case where someone tried to use a
        # class-referenced type variable as a type alias.  It's easier to catch
        # that one in checkmember.py
        if node.kind == TVAR:
            api.fail(
                'Type variable "{}" is invalid as target for type alias'.
                format(node.fullname), node)
            return None
        if not (isinstance(node.node, TypeInfo)
                or node.fullname == 'typing.Any' or node.kind == TYPE_ALIAS):
            return None
    elif isinstance(node, IndexExpr):
        base = node.base
        if isinstance(base, RefExpr):
            if not (isinstance(base.node, TypeInfo) or base.fullname
                    in type_constructors or base.kind == TYPE_ALIAS):
                return None
            # Enums can't be generic, and without this check we may incorrectly interpret indexing
            # an Enum class as creating a type alias.
            if isinstance(base.node, TypeInfo) and base.node.is_enum:
                return None
        else:
            return None
    elif isinstance(node, CallExpr):
        if (isinstance(node.callee, NameExpr) and len(node.args) == 1
                and isinstance(node.args[0], NameExpr)):
            call = api.lookup_qualified(node.callee.name, node.callee)
            arg = api.lookup_qualified(node.args[0].name, node.args[0])
            if (call is not None and call.node
                    and call.node.fullname() == 'builtins.type'
                    and arg is not None and arg.node
                    and arg.node.fullname() == 'builtins.None'):
                return NoneTyp(), set()
            return None
        return None
    else:
        return None

    # It's a type alias (though it may be an invalid one).
    try:
        type = expr_to_unanalyzed_type(node)
    except TypeTranslationError:
        api.fail('Invalid type alias', node)
        return None
    analyzer = TypeAnalyser(api,
                            tvar_scope,
                            plugin,
                            options,
                            is_typeshed_stub,
                            aliasing=True,
                            allow_unnormalized=allow_unnormalized,
                            warn_bound_tvar=warn_bound_tvar)
    analyzer.in_dynamic_func = in_dynamic_func
    analyzer.global_scope = global_scope
    res = type.accept(analyzer)
    return res, analyzer.aliases_used
示例#9
0
def analyze_type_alias(node: Expression,
                       lookup_func: Callable[[str, Context], Optional[SymbolTableNode]],
                       lookup_fqn_func: Callable[[str], SymbolTableNode],
                       tvar_scope: TypeVarScope,
                       fail_func: Callable[[str, Context], None],
                       note_func: Callable[[str, Context], None],
                       plugin: Plugin,
                       options: Options,
                       is_typeshed_stub: bool,
                       allow_unnormalized: bool = False,
                       in_dynamic_func: bool = False,
                       global_scope: bool = True,
                       warn_bound_tvar: bool = False) -> Optional[Type]:
    """Return type if node is valid as a type alias rvalue.

    Return None otherwise. 'node' must have been semantically analyzed.
    """
    # Quickly return None if the expression doesn't look like a type. Note
    # that we don't support straight string literals as type aliases
    # (only string literals within index expressions).
    if isinstance(node, RefExpr):
        # Note that this misses the case where someone tried to use a
        # class-referenced type variable as a type alias.  It's easier to catch
        # that one in checkmember.py
        if node.kind == TVAR:
            fail_func('Type variable "{}" is invalid as target for type alias'.format(
                node.fullname), node)
            return None
        if not (isinstance(node.node, TypeInfo) or
                node.fullname == 'typing.Any' or
                node.kind == TYPE_ALIAS):
            return None
    elif isinstance(node, IndexExpr):
        base = node.base
        if isinstance(base, RefExpr):
            if not (isinstance(base.node, TypeInfo) or
                    base.fullname in type_constructors or
                    base.kind == TYPE_ALIAS):
                return None
            # Enums can't be generic, and without this check we may incorrectly interpret indexing
            # an Enum class as creating a type alias.
            if isinstance(base.node, TypeInfo) and base.node.is_enum:
                return None
        else:
            return None
    elif isinstance(node, CallExpr):
        if (isinstance(node.callee, NameExpr) and len(node.args) == 1 and
                isinstance(node.args[0], NameExpr)):
            call = lookup_func(node.callee.name, node.callee)
            arg = lookup_func(node.args[0].name, node.args[0])
            if (call is not None and call.node and call.node.fullname() == 'builtins.type' and
                    arg is not None and arg.node and arg.node.fullname() == 'builtins.None'):
                return NoneTyp()
            return None
        return None
    else:
        return None

    # It's a type alias (though it may be an invalid one).
    try:
        type = expr_to_unanalyzed_type(node)
    except TypeTranslationError:
        fail_func('Invalid type alias', node)
        return None
    analyzer = TypeAnalyser(lookup_func, lookup_fqn_func, tvar_scope, fail_func, note_func,
                            plugin, options, is_typeshed_stub, aliasing=True,
                            allow_unnormalized=allow_unnormalized, warn_bound_tvar=warn_bound_tvar)
    analyzer.in_dynamic_func = in_dynamic_func
    analyzer.global_scope = global_scope
    return type.accept(analyzer)
def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
                                 auto_attribs: bool,
                                 kw_only: bool,
                                 lhs: NameExpr,
                                 rvalue: CallExpr,
                                 stmt: AssignmentStmt) -> Optional[Attribute]:
    """Return an Attribute from the assignment or None if you can't make one."""
    if auto_attribs and not stmt.new_syntax:
        # auto_attribs requires an annotation on *every* attr.ib.
        assert lhs.node is not None
        ctx.api.msg.need_annotation_for_var(lhs.node, stmt)
        return None

    if len(stmt.lvalues) > 1:
        ctx.api.fail("Too many names for one attribute", stmt)
        return None

    # This is the type that belongs in the __init__ method for this attrib.
    init_type = stmt.type

    # Read all the arguments from the call.
    init = _get_bool_argument(ctx, rvalue, 'init', True)
    # Note: If the class decorator says kw_only=True the attribute is ignored.
    # See https://github.com/python-attrs/attrs/issues/481 for explanation.
    kw_only |= _get_bool_argument(ctx, rvalue, 'kw_only', False)
    if kw_only and ctx.api.options.python_version[0] < 3:
        ctx.api.fail(KW_ONLY_PYTHON_2_UNSUPPORTED, stmt)
        return None

    # TODO: Check for attr.NOTHING
    attr_has_default = bool(_get_argument(rvalue, 'default'))
    attr_has_factory = bool(_get_argument(rvalue, 'factory'))

    if attr_has_default and attr_has_factory:
        ctx.api.fail("Can't pass both `default` and `factory`.", rvalue)
    elif attr_has_factory:
        attr_has_default = True

    # If the type isn't set through annotation but is passed through `type=` use that.
    type_arg = _get_argument(rvalue, 'type')
    if type_arg and not init_type:
        try:
            un_type = expr_to_unanalyzed_type(type_arg)
        except TypeTranslationError:
            ctx.api.fail('Invalid argument to type', type_arg)
        else:
            init_type = ctx.api.anal_type(un_type)
            if init_type and isinstance(lhs.node, Var) and not lhs.node.type:
                # If there is no annotation, add one.
                lhs.node.type = init_type
                lhs.is_inferred_def = False

    # Note: convert is deprecated but works the same as converter.
    converter = _get_argument(rvalue, 'converter')
    convert = _get_argument(rvalue, 'convert')
    if convert and converter:
        ctx.api.fail("Can't pass both `convert` and `converter`.", rvalue)
    elif convert:
        ctx.api.fail("convert is deprecated, use converter", rvalue)
        converter = convert
    converter_info = _parse_converter(ctx, converter)

    name = unmangle(lhs.name)
    return Attribute(name, ctx.cls.info, attr_has_default, init,
                     kw_only, converter_info, stmt, init_type)
示例#11
0
def analyze_type_alias(node: Expression,
                       lookup_func: Callable[[str, Context],
                                             Optional[SymbolTableNode]],
                       lookup_fqn_func: Callable[[str], SymbolTableNode],
                       tvar_scope: TypeVarScope,
                       fail_func: Callable[[str, Context], None],
                       note_func: Callable[[str, Context], None],
                       plugin: Plugin,
                       options: Options,
                       is_typeshed_stub: bool,
                       allow_unnormalized: bool = False,
                       in_dynamic_func: bool = False,
                       global_scope: bool = True,
                       warn_bound_tvar: bool = False) -> Optional[Type]:
    """Return type if node is valid as a type alias rvalue.

    Return None otherwise. 'node' must have been semantically analyzed.
    """
    # Quickly return None if the expression doesn't look like a type. Note
    # that we don't support straight string literals as type aliases
    # (only string literals within index expressions).
    if isinstance(node, RefExpr):
        # Note that this misses the case where someone tried to use a
        # class-referenced type variable as a type alias.  It's easier to catch
        # that one in checkmember.py
        if node.kind == TVAR:
            fail_func(
                'Type variable "{}" is invalid as target for type alias'.
                format(node.fullname), node)
            return None
        if not (isinstance(node.node, TypeInfo)
                or node.fullname == 'typing.Any' or node.kind == TYPE_ALIAS):
            return None
    elif isinstance(node, IndexExpr):
        base = node.base
        if isinstance(base, RefExpr):
            if not (isinstance(base.node, TypeInfo) or base.fullname
                    in type_constructors or base.kind == TYPE_ALIAS):
                return None
            # Enums can't be generic, and without this check we may incorrectly interpret indexing
            # an Enum class as creating a type alias.
            if isinstance(base.node, TypeInfo) and base.node.is_enum:
                return None
        else:
            return None
    elif isinstance(node, CallExpr):
        if (isinstance(node.callee, NameExpr) and len(node.args) == 1
                and isinstance(node.args[0], NameExpr)):
            call = lookup_func(node.callee.name, node.callee)
            arg = lookup_func(node.args[0].name, node.args[0])
            if (call is not None and call.node
                    and call.node.fullname() == 'builtins.type'
                    and arg is not None and arg.node
                    and arg.node.fullname() == 'builtins.None'):
                return NoneTyp()
            return None
        return None
    else:
        return None

    # It's a type alias (though it may be an invalid one).
    try:
        type = expr_to_unanalyzed_type(node)
    except TypeTranslationError:
        fail_func('Invalid type alias', node)
        return None
    analyzer = TypeAnalyser(lookup_func,
                            lookup_fqn_func,
                            tvar_scope,
                            fail_func,
                            note_func,
                            plugin,
                            options,
                            is_typeshed_stub,
                            aliasing=True,
                            allow_unnormalized=allow_unnormalized,
                            warn_bound_tvar=warn_bound_tvar)
    analyzer.in_dynamic_func = in_dynamic_func
    analyzer.global_scope = global_scope
    return type.accept(analyzer)
示例#12
0
文件: attrs.py 项目: Michael0x2a/mypy
def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
                                 auto_attribs: bool,
                                 kw_only: bool,
                                 lhs: NameExpr,
                                 rvalue: CallExpr,
                                 stmt: AssignmentStmt) -> Optional[Attribute]:
    """Return an Attribute from the assignment or None if you can't make one."""
    if auto_attribs and not stmt.new_syntax:
        # auto_attribs requires an annotation on *every* attr.ib.
        assert lhs.node is not None
        ctx.api.msg.need_annotation_for_var(lhs.node, stmt)
        return None

    if len(stmt.lvalues) > 1:
        ctx.api.fail("Too many names for one attribute", stmt)
        return None

    # This is the type that belongs in the __init__ method for this attrib.
    init_type = stmt.type

    # Read all the arguments from the call.
    init = _get_bool_argument(ctx, rvalue, 'init', True)
    # Note: If the class decorator says kw_only=True the attribute is ignored.
    # See https://github.com/python-attrs/attrs/issues/481 for explanation.
    kw_only |= _get_bool_argument(ctx, rvalue, 'kw_only', False)
    if kw_only and ctx.api.options.python_version[0] < 3:
        ctx.api.fail(KW_ONLY_PYTHON_2_UNSUPPORTED, stmt)
        return None

    # TODO: Check for attr.NOTHING
    attr_has_default = bool(_get_argument(rvalue, 'default'))
    attr_has_factory = bool(_get_argument(rvalue, 'factory'))

    if attr_has_default and attr_has_factory:
        ctx.api.fail("Can't pass both `default` and `factory`.", rvalue)
    elif attr_has_factory:
        attr_has_default = True

    # If the type isn't set through annotation but is passed through `type=` use that.
    type_arg = _get_argument(rvalue, 'type')
    if type_arg and not init_type:
        try:
            un_type = expr_to_unanalyzed_type(type_arg)
        except TypeTranslationError:
            ctx.api.fail('Invalid argument to type', type_arg)
        else:
            init_type = ctx.api.anal_type(un_type)
            if init_type and isinstance(lhs.node, Var) and not lhs.node.type:
                # If there is no annotation, add one.
                lhs.node.type = init_type
                lhs.is_inferred_def = False

    # Note: convert is deprecated but works the same as converter.
    converter = _get_argument(rvalue, 'converter')
    convert = _get_argument(rvalue, 'convert')
    if convert and converter:
        ctx.api.fail("Can't pass both `convert` and `converter`.", rvalue)
    elif convert:
        ctx.api.fail("convert is deprecated, use converter", rvalue)
        converter = convert
    converter_info = _parse_converter(ctx, converter)

    name = unmangle(lhs.name)
    return Attribute(name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt)
示例#13
0
文件: typeanal.py 项目: sixolet/mypy
def analyze_type_alias(node: Expression,
                       api: SemanticAnalyzerInterface,
                       tvar_scope: TypeVarScope,
                       plugin: Plugin,
                       options: Options,
                       is_typeshed_stub: bool,
                       allow_unnormalized: bool = False,
                       in_dynamic_func: bool = False,
                       global_scope: bool = True,
                       warn_bound_tvar: bool = False) -> Optional[Tuple[Type, Set[str]]]:
    """Analyze r.h.s. of a (potential) type alias definition.

    If `node` is valid as a type alias rvalue, return the resulting type and a set of
    full names of type aliases it depends on (directly or indirectly).
    Return None otherwise. 'node' must have been semantically analyzed.
    """
    # Quickly return None if the expression doesn't look like a type. Note
    # that we don't support straight string literals as type aliases
    # (only string literals within index expressions).
    if isinstance(node, RefExpr):
        # Note that this misses the case where someone tried to use a
        # class-referenced type variable as a type alias.  It's easier to catch
        # that one in checkmember.py
        if node.kind == TVAR:
            api.fail('Type variable "{}" is invalid as target for type alias'.format(
                node.fullname), node)
            return None
        if not (isinstance(node.node, TypeInfo) or
                node.fullname == 'typing.Any' or
                node.kind == TYPE_ALIAS):
            return None
    elif isinstance(node, IndexExpr):
        base = node.base
        if isinstance(base, RefExpr):
            if not (isinstance(base.node, TypeInfo) or
                    base.fullname in type_constructors or
                    base.kind == TYPE_ALIAS):
                return None
            # Enums can't be generic, and without this check we may incorrectly interpret indexing
            # an Enum class as creating a type alias.
            if isinstance(base.node, TypeInfo) and base.node.is_enum:
                return None
        else:
            return None
    elif isinstance(node, CallExpr):
        if (isinstance(node.callee, NameExpr) and len(node.args) == 1 and
                isinstance(node.args[0], NameExpr)):
            call = api.lookup_qualified(node.callee.name, node.callee)
            arg = api.lookup_qualified(node.args[0].name, node.args[0])
            if (call is not None and call.node and call.node.fullname() == 'builtins.type' and
                    arg is not None and arg.node and arg.node.fullname() == 'builtins.None'):
                return NoneTyp(), set()
            return None
        return None
    else:
        return None

    # It's a type alias (though it may be an invalid one).
    try:
        type = expr_to_unanalyzed_type(node)
    except TypeTranslationError:
        api.fail('Invalid type alias', node)
        return None
    analyzer = TypeAnalyser(api, tvar_scope, plugin, options, is_typeshed_stub, aliasing=True,
                            allow_unnormalized=allow_unnormalized, warn_bound_tvar=warn_bound_tvar)
    analyzer.in_dynamic_func = in_dynamic_func
    analyzer.global_scope = global_scope
    res = type.accept(analyzer)
    return res, analyzer.aliases_used
示例#14
0
    def _field_from_field_def(
        self,
        stmt: nodes.AssignmentStmt,
        name: nodes.NameExpr,
        sym: nodes.SymbolTableNode,
    ) -> Optional[Field]:
        ctx = self._ctx

        rhs = stmt.rvalue

        if isinstance(rhs, nodes.CastExpr):
            rhs = rhs.expr

        if not isinstance(rhs, nodes.CallExpr):
            return None

        fdef = rhs.callee

        ftype = None
        if (
            isinstance(fdef, nodes.IndexExpr)
            and isinstance(fdef.analyzed, nodes.TypeApplication)
        ):
            # Explicitly typed Field declaration
            ctor = fdef.analyzed.expr
            if len(fdef.analyzed.types) > 1:
                ctx.api.fail('too many type arguments to Field', fdef)
            ftype = fdef.analyzed.types[0]
        else:
            ctor = fdef
            ftype = None

        if (
            not isinstance(ctor, nodes.RefExpr)
            or ctor.fullname not in self._field_makers
        ):
            return None

        type_arg = rhs.args[0]

        deflt = self._get_default(rhs)

        if ftype is None:
            try:
                un_type = exprtotype.expr_to_unanalyzed_type(type_arg)
            except exprtotype.TypeTranslationError:
                ctx.api.fail('Cannot resolve schema field type', type_arg)
            else:
                ftype = ctx.api.anal_type(un_type)
            if ftype is None:
                raise DeferException

            is_optional = (
                isinstance(deflt, nodes.NameExpr)
                and deflt.fullname == 'builtins.None'
            )
            if is_optional:
                ftype = types.UnionType.make_union(
                    [ftype, types.NoneType()],
                    line=ftype.line,
                    column=ftype.column,
                )

        assert isinstance(name.node, nodes.Var)
        name.node.type = ftype

        return Field(
            name=name.name,
            has_explicit_accessor=self._has_explicit_field_accessor(name.name),
            has_default=deflt is not None,
            line=stmt.line,
            column=stmt.column,
            type=ftype,
        )