Example #1
0
    def analyze_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool
                                    ) -> Tuple[bool, Optional[TypeInfo]]:
        """Analyze if given class definition can be a named tuple definition.

        Return a tuple where first item indicates whether this can possibly be a named tuple,
        and the second item is the corresponding TypeInfo (may be None if not ready and should be
        deferred).
        """
        for base_expr in defn.base_type_exprs:
            if isinstance(base_expr, RefExpr):
                self.api.accept(base_expr)
                if base_expr.fullname == 'typing.NamedTuple':
                    result = self.check_namedtuple_classdef(defn, is_stub_file)
                    if result is None:
                        # This is a valid named tuple, but some types are incomplete.
                        return True, None
                    items, types, default_items = result
                    info = self.build_namedtuple_typeinfo(
                        defn.name, items, types, default_items, defn.line)
                    defn.info = info
                    defn.analyzed = NamedTupleExpr(info, is_typed=True)
                    defn.analyzed.line = defn.line
                    defn.analyzed.column = defn.column
                    # All done: this is a valid named tuple with all types known.
                    return True, info
        # This can't be a valid named tuple.
        return False, None
Example #2
0
    def analyze_namedtuple_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[TypeInfo]]:
        """Analyze if given class definition can be a named tuple definition.

        Return a tuple where first item indicates whether this can possibly be a named tuple,
        and the second item is the corresponding TypeInfo (may be None if not ready and should be
        deferred).
        """
        for base_expr in defn.base_type_exprs:
            if isinstance(base_expr, RefExpr):
                self.api.accept(base_expr)
                if base_expr.fullname == 'typing.NamedTuple':
                    result = self.check_namedtuple_classdef(defn)
                    if result is None:
                        # This is a valid named tuple, but some types are incomplete.
                        return True, None
                    items, types, default_items = result
                    info = self.build_namedtuple_typeinfo(
                        defn.name, items, types, default_items, defn.line)
                    defn.info = info
                    defn.analyzed = NamedTupleExpr(info, is_typed=True)
                    defn.analyzed.line = defn.line
                    defn.analyzed.column = defn.column
                    # All done: this is a valid named tuple with all types known.
                    return True, info
        # This can't be a valid named tuple.
        return False, None
Example #3
0
 def analyze_namedtuple_classdef(self, defn: ClassDef) -> Optional[TypeInfo]:
     # special case for NamedTuple
     for base_expr in defn.base_type_exprs:
         if isinstance(base_expr, RefExpr):
             self.api.accept(base_expr)
             if base_expr.fullname == 'typing.NamedTuple':
                 node = self.api.lookup(defn.name, defn)
                 if node is not None:
                     node.kind = GDEF  # TODO in process_namedtuple_definition also applies here
                     items, types, default_items = self.check_namedtuple_classdef(defn)
                     info = self.build_namedtuple_typeinfo(
                         defn.name, items, types, default_items)
                     node.node = info
                     defn.info.replaced = info
                     defn.info = info
                     defn.analyzed = NamedTupleExpr(info, is_typed=True)
                     defn.analyzed.line = defn.line
                     defn.analyzed.column = defn.column
                     return info
     return None
Example #4
0
 def analyze_namedtuple_classdef(self, defn: ClassDef) -> Optional[TypeInfo]:
     # special case for NamedTuple
     for base_expr in defn.base_type_exprs:
         if isinstance(base_expr, RefExpr):
             self.api.accept(base_expr)
             if base_expr.fullname == 'typing.NamedTuple':
                 node = self.api.lookup(defn.name, defn)
                 if node is not None:
                     node.kind = GDEF  # TODO in process_namedtuple_definition also applies here
                     items, types, default_items = self.check_namedtuple_classdef(defn)
                     info = self.build_namedtuple_typeinfo(
                         defn.name, items, types, default_items)
                     node.node = info
                     defn.info.replaced = info
                     defn.info = info
                     defn.analyzed = NamedTupleExpr(info, is_typed=True)
                     defn.analyzed.line = defn.line
                     defn.analyzed.column = defn.column
                     return info
     return None
Example #5
0
 def analyze_typeddict_classdef(self, defn: ClassDef) -> bool:
     # special case for TypedDict
     possible = False
     for base_expr in defn.base_type_exprs:
         if isinstance(base_expr, RefExpr):
             self.api.accept(base_expr)
             if (base_expr.fullname == 'mypy_extensions.TypedDict' or
                     self.is_typeddict(base_expr)):
                 possible = True
     if possible:
         node = self.api.lookup(defn.name, defn)
         if node is not None:
             node.kind = GDEF  # TODO in process_namedtuple_definition also applies here
             if (len(defn.base_type_exprs) == 1 and
                     isinstance(defn.base_type_exprs[0], RefExpr) and
                     defn.base_type_exprs[0].fullname == 'mypy_extensions.TypedDict'):
                 # Building a new TypedDict
                 fields, types, required_keys = self.check_typeddict_classdef(defn)
                 info = self.build_typeddict_typeinfo(defn.name, fields, types, required_keys)
                 defn.info.replaced = info
                 defn.info = info
                 node.node = info
                 defn.analyzed = TypedDictExpr(info)
                 defn.analyzed.line = defn.line
                 defn.analyzed.column = defn.column
                 return True
             # Extending/merging existing TypedDicts
             if any(not isinstance(expr, RefExpr) or
                    expr.fullname != 'mypy_extensions.TypedDict' and
                    not self.is_typeddict(expr) for expr in defn.base_type_exprs):
                 self.fail("All bases of a new TypedDict must be TypedDict types", defn)
             typeddict_bases = list(filter(self.is_typeddict, defn.base_type_exprs))
             keys = []  # type: List[str]
             types = []
             required_keys = set()
             for base in typeddict_bases:
                 assert isinstance(base, RefExpr)
                 assert isinstance(base.node, TypeInfo)
                 assert isinstance(base.node.typeddict_type, TypedDictType)
                 base_typed_dict = base.node.typeddict_type
                 base_items = base_typed_dict.items
                 valid_items = base_items.copy()
                 for key in base_items:
                     if key in keys:
                         self.fail('Cannot overwrite TypedDict field "{}" while merging'
                                   .format(key), defn)
                         valid_items.pop(key)
                 keys.extend(valid_items.keys())
                 types.extend(valid_items.values())
                 required_keys.update(base_typed_dict.required_keys)
             new_keys, new_types, new_required_keys = self.check_typeddict_classdef(defn, keys)
             keys.extend(new_keys)
             types.extend(new_types)
             required_keys.update(new_required_keys)
             info = self.build_typeddict_typeinfo(defn.name, keys, types, required_keys)
             defn.info.replaced = info
             defn.info = info
             node.node = info
             defn.analyzed = TypedDictExpr(info)
             defn.analyzed.line = defn.line
             defn.analyzed.column = defn.column
             return True
     return False
Example #6
0
    def analyze_typeddict_classdef(
            self, defn: ClassDef) -> Tuple[bool, Optional[TypeInfo]]:
        """Analyze a class that may define a TypedDict.

        Assume that base classes have been analyzed already.

        Note: Unlike normal classes, we won't create a TypeInfo until
        the whole definition of the TypeDict (including the body and all
        key names and types) is complete.  This is mostly because we
        store the corresponding TypedDictType in the TypeInfo.

        Return (is this a TypedDict, new TypeInfo). Specifics:
         * If we couldn't finish due to incomplete reference anywhere in
           the definition, return (True, None).
         * If this is not a TypedDict, return (False, None).
        """
        possible = False
        for base_expr in defn.base_type_exprs:
            if isinstance(base_expr, RefExpr):
                self.api.accept(base_expr)
                if base_expr.fullname in TPDICT_NAMES or self.is_typeddict(
                        base_expr):
                    possible = True
        if possible:
            if (len(defn.base_type_exprs) == 1
                    and isinstance(defn.base_type_exprs[0], RefExpr)
                    and defn.base_type_exprs[0].fullname in TPDICT_NAMES):
                # Building a new TypedDict
                fields, types, required_keys = self.analyze_typeddict_classdef_fields(
                    defn)
                if fields is None:
                    return True, None  # Defer
                info = self.build_typeddict_typeinfo(defn.name, fields, types,
                                                     required_keys)
                defn.analyzed = TypedDictExpr(info)
                defn.analyzed.line = defn.line
                defn.analyzed.column = defn.column
                return True, info
            # Extending/merging existing TypedDicts
            if any(not isinstance(expr, RefExpr) or expr.fullname not in
                   TPDICT_NAMES and not self.is_typeddict(expr)
                   for expr in defn.base_type_exprs):
                self.fail(
                    "All bases of a new TypedDict must be TypedDict types",
                    defn)
            typeddict_bases = list(
                filter(self.is_typeddict, defn.base_type_exprs))
            keys = []  # type: List[str]
            types = []
            required_keys = set()
            for base in typeddict_bases:
                assert isinstance(base, RefExpr)
                assert isinstance(base.node, TypeInfo)
                assert isinstance(base.node.typeddict_type, TypedDictType)
                base_typed_dict = base.node.typeddict_type
                base_items = base_typed_dict.items
                valid_items = base_items.copy()
                for key in base_items:
                    if key in keys:
                        self.fail(
                            'Cannot overwrite TypedDict field "{}" while merging'
                            .format(key), defn)
                        valid_items.pop(key)
                keys.extend(valid_items.keys())
                types.extend(valid_items.values())
                required_keys.update(base_typed_dict.required_keys)
            new_keys, new_types, new_required_keys = self.analyze_typeddict_classdef_fields(
                defn, keys)
            if new_keys is None:
                return True, None  # Defer
            keys.extend(new_keys)
            types.extend(new_types)
            required_keys.update(new_required_keys)
            info = self.build_typeddict_typeinfo(defn.name, keys, types,
                                                 required_keys)
            defn.analyzed = TypedDictExpr(info)
            defn.analyzed.line = defn.line
            defn.analyzed.column = defn.column
            return True, info
        return False, None
Example #7
0
 def analyze_typeddict_classdef(self, defn: ClassDef) -> bool:
     # special case for TypedDict
     possible = False
     for base_expr in defn.base_type_exprs:
         if isinstance(base_expr, RefExpr):
             self.api.accept(base_expr)
             if (base_expr.fullname == 'mypy_extensions.TypedDict' or
                     self.is_typeddict(base_expr)):
                 possible = True
     if possible:
         node = self.api.lookup(defn.name, defn)
         if node is not None:
             node.kind = GDEF  # TODO in process_namedtuple_definition also applies here
             if (len(defn.base_type_exprs) == 1 and
                     isinstance(defn.base_type_exprs[0], RefExpr) and
                     defn.base_type_exprs[0].fullname == 'mypy_extensions.TypedDict'):
                 # Building a new TypedDict
                 fields, types, required_keys = self.check_typeddict_classdef(defn)
                 info = self.build_typeddict_typeinfo(defn.name, fields, types, required_keys)
                 defn.info.replaced = info
                 defn.info = info
                 node.node = info
                 defn.analyzed = TypedDictExpr(info)
                 defn.analyzed.line = defn.line
                 defn.analyzed.column = defn.column
                 return True
             # Extending/merging existing TypedDicts
             if any(not isinstance(expr, RefExpr) or
                    expr.fullname != 'mypy_extensions.TypedDict' and
                    not self.is_typeddict(expr) for expr in defn.base_type_exprs):
                 self.fail("All bases of a new TypedDict must be TypedDict types", defn)
             typeddict_bases = list(filter(self.is_typeddict, defn.base_type_exprs))
             keys = []  # type: List[str]
             types = []
             required_keys = set()
             for base in typeddict_bases:
                 assert isinstance(base, RefExpr)
                 assert isinstance(base.node, TypeInfo)
                 assert isinstance(base.node.typeddict_type, TypedDictType)
                 base_typed_dict = base.node.typeddict_type
                 base_items = base_typed_dict.items
                 valid_items = base_items.copy()
                 for key in base_items:
                     if key in keys:
                         self.fail('Cannot overwrite TypedDict field "{}" while merging'
                                   .format(key), defn)
                         valid_items.pop(key)
                 keys.extend(valid_items.keys())
                 types.extend(valid_items.values())
                 required_keys.update(base_typed_dict.required_keys)
             new_keys, new_types, new_required_keys = self.check_typeddict_classdef(defn, keys)
             keys.extend(new_keys)
             types.extend(new_types)
             required_keys.update(new_required_keys)
             info = self.build_typeddict_typeinfo(defn.name, keys, types, required_keys)
             defn.info.replaced = info
             defn.info = info
             node.node = info
             defn.analyzed = TypedDictExpr(info)
             defn.analyzed.line = defn.line
             defn.analyzed.column = defn.column
             return True
     return False
Example #8
0
    def analyze_typeddict_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[TypeInfo]]:
        """Analyze a class that may define a TypedDict.

        Assume that base classes have been analyzed already.

        Note: Unlike normal classes, we won't create a TypeInfo until
        the whole definition of the TypeDict (including the body and all
        key names and types) is complete.  This is mostly because we
        store the corresponding TypedDictType in the TypeInfo.

        Return (is this a TypedDict, new TypeInfo). Specifics:
         * If we couldn't finish due to incomplete reference anywhere in
           the definition, return (True, None).
         * If this is not a TypedDict, return (False, None).
        """
        possible = False
        for base_expr in defn.base_type_exprs:
            if isinstance(base_expr, RefExpr):
                self.api.accept(base_expr)
                if (base_expr.fullname == 'mypy_extensions.TypedDict' or
                        self.is_typeddict(base_expr)):
                    possible = True
        if possible:
            if (len(defn.base_type_exprs) == 1 and
                    isinstance(defn.base_type_exprs[0], RefExpr) and
                    defn.base_type_exprs[0].fullname == 'mypy_extensions.TypedDict'):
                # Building a new TypedDict
                fields, types, required_keys = self.analyze_typeddict_classdef_fields(defn)
                if fields is None:
                    return True, None  # Defer
                info = self.build_typeddict_typeinfo(defn.name, fields, types, required_keys)
                defn.analyzed = TypedDictExpr(info)
                defn.analyzed.line = defn.line
                defn.analyzed.column = defn.column
                return True, info
            # Extending/merging existing TypedDicts
            if any(not isinstance(expr, RefExpr) or
                   expr.fullname != 'mypy_extensions.TypedDict' and
                   not self.is_typeddict(expr) for expr in defn.base_type_exprs):
                self.fail("All bases of a new TypedDict must be TypedDict types", defn)
            typeddict_bases = list(filter(self.is_typeddict, defn.base_type_exprs))
            keys = []  # type: List[str]
            types = []
            required_keys = set()
            for base in typeddict_bases:
                assert isinstance(base, RefExpr)
                assert isinstance(base.node, TypeInfo)
                assert isinstance(base.node.typeddict_type, TypedDictType)
                base_typed_dict = base.node.typeddict_type
                base_items = base_typed_dict.items
                valid_items = base_items.copy()
                for key in base_items:
                    if key in keys:
                        self.fail('Cannot overwrite TypedDict field "{}" while merging'
                                  .format(key), defn)
                        valid_items.pop(key)
                keys.extend(valid_items.keys())
                types.extend(valid_items.values())
                required_keys.update(base_typed_dict.required_keys)
            new_keys, new_types, new_required_keys = self.analyze_typeddict_classdef_fields(defn,
                                                                                            keys)
            if new_keys is None:
                return True, None  # Defer
            keys.extend(new_keys)
            types.extend(new_types)
            required_keys.update(new_required_keys)
            info = self.build_typeddict_typeinfo(defn.name, keys, types, required_keys)
            defn.analyzed = TypedDictExpr(info)
            defn.analyzed.line = defn.line
            defn.analyzed.column = defn.column
            return True, info
        return False, None