def visit_tuple_type(self, t: TupleType) -> Type: if isinstance(self.s, TupleType) and self.s.length() == t.length(): items = [] # type: List[Type] for i in range(t.length()): items.append(self.meet(t.items[i], self.s.items[i])) # TODO: What if the fallbacks are different? return TupleType(items, t.fallback) else: return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: if isinstance(self.s, TupleType) and self.s.length() == t.length(): items = [] # type: List[Type] for i in range(t.length()): items.append(self.join(t.items[i], self.s.items[i])) fallback = join_instances(self.s.fallback, t.fallback) assert isinstance(fallback, Instance) return TupleType(items, fallback) else: return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: if isinstance(self.s, TupleType) and self.s.length() == t.length(): items = [] # type: List[Type] for i in range(t.length()): items.append(self.join(t.items[i], self.s.items[i])) # join fallback types if they are different from typing import cast return TupleType(items, cast(Instance, join_instances(self.s.fallback, t.fallback))) else: return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: if (isinstance(self.s, TupleType) and cast(TupleType, self.s).length() == t.length()): items = [] # type: List[Type] for i in range(t.length()): items.append(self.join(t.items[i], (cast(TupleType, self.s)).items[i])) return TupleType(items) else: return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: if isinstance(self.s, TupleType) and self.s.length() == t.length(): items = [] # type: List[Type] for i in range(t.length()): items.append(self.meet(t.items[i], self.s.items[i])) # TODO: What if the fallbacks are different? return TupleType(items, t.fallback) # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>]. elif (isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.tuple' and self.s.args): return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items]) else: return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: if isinstance(self.s, TupleType) and self.s.length() == t.length(): items = [] # type: List[Type] for i in range(t.length()): items.append(self.meet(t.items[i], self.s.items[i])) # TODO: What if the fallbacks are different? return TupleType(items, tuple_fallback(t)) elif isinstance(self.s, Instance): # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>]. if self.s.type.fullname() == 'builtins.tuple' and self.s.args: return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items]) elif is_proper_subtype(t, self.s): # A named tuple that inherits from a normal class return t return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: return TupleType( self.translate_types(t.items), # TODO: This appears to be unsafe. cast(Any, t.fallback.accept(self)), t.line, t.column)
def visit_tuple_type(self, t: TupleType) -> ProperType: if isinstance(self.s, TupleType) and self.s.length() == t.length(): items: List[Type] = [] for i in range(t.length()): items.append(self.meet(t.items[i], self.s.items[i])) # TODO: What if the fallbacks are different? return TupleType(items, tuple_fallback(t)) elif isinstance(self.s, Instance): # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>]. if self.s.type.fullname == 'builtins.tuple' and self.s.args: return t.copy_modified( items=[meet_types(it, self.s.args[0]) for it in t.items]) elif is_proper_subtype(t, self.s): # A named tuple that inherits from a normal class return t return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: # Types such as (t1, t2, ...) only allowed in assignment statements. They'll # generate errors elsewhere, and Tuple[t1, t2, ...] must be used instead. if t.implicit and not self.allow_tuple_literal: self.fail('Invalid tuple literal type', t) return AnyType() star_count = sum(1 for item in t.items if isinstance(item, StarType)) if star_count > 1: self.fail('At most one star type allowed in a tuple', t) if t.implicit: return TupleType([AnyType() for _ in t.items], self.named_type('builtins.tuple'), t.line) else: return AnyType() fallback = t.fallback if t.fallback else self.named_type( 'builtins.tuple', [AnyType()]) return TupleType(self.anal_array(t.items), fallback, t.line)
def visit_tuple_type(self, t: TupleType) -> Type: if isinstance(self.s, TupleType) and self.s.length() == t.length(): items = [] # type: List[Type] for i in range(t.length()): items.append(self.meet(t.items[i], self.s.items[i])) # TODO: What if the fallbacks are different? return TupleType(items, t.fallback) # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>]. elif (isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.tuple' and self.s.args): return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items]) elif (isinstance(self.s, Instance) and t.fallback.type == self.s.type): # Uh oh, a broken named tuple type (https://github.com/python/mypy/issues/3016). # Do something reasonable until that bug is fixed. return t else: return self.default(self.s)
def visit_tuple_type(self, t: TupleType) -> Type: star_count = sum(1 for item in t.items if isinstance(item, StarType)) if star_count > 1: self.fail('At most one star type allowed in a tuple', t) return AnyType() fallback = t.fallback if t.fallback else self.builtin_type( 'builtins.tuple') return TupleType(self.anal_array(t.items), fallback, t.line)
def visit_unbound_type(self, t: UnboundType) -> Type: sym = self.lookup(t.name, t) if sym is not None: if sym.kind == TVAR: if len(t.args) > 0: self.fail( 'Type variable "{}" used with arguments'.format( t.name), t) if t.repr: rep = TypeVarRepr(t.repr.components[0]) else: rep = None values = cast(TypeVarExpr, sym.node).values return TypeVar(t.name, sym.tvar_id, values, False, t.line, rep) elif sym.node.fullname() == 'builtins.None': return Void() elif sym.node.fullname() == 'typing.Any': return AnyType() elif sym.node.fullname() == 'typing.Tuple': return TupleType(self.anal_array(t.args)) elif sym.node.fullname() == 'typing.Union': return UnionType(self.anal_array(t.args)) elif sym.node.fullname() == 'typing.Function': return self.analyze_function_type(t) elif not isinstance(sym.node, TypeInfo): name = sym.fullname if name is None: name = sym.node.name() if sym.node in self.stored_vars: return self.stored_vars[sym.node] self.fail('Invalid type "{}"'.format(name), t) return t info = cast(TypeInfo, sym.node) if len(t.args) > 0 and info.fullname() == 'builtins.tuple': return TupleType(self.anal_array(t.args), t.line, t.repr) else: # Analyze arguments and construct Instance type. The # number of type arguments and their values are # checked only later, since we do not always know the # valid count at this point. Thus we may construct an # Instance with an invalid number of type arguments. return Instance(info, self.anal_array(t.args), t.line, t.repr) else: return t
def make_oneoff_named_tuple(api: TypeChecker, name: str, fields: 'OrderedDict[str, MypyType]') -> TupleType: current_module = get_current_module(api) namedtuple_info = add_new_class_for_module( current_module, name, bases=[api.named_generic_type('typing.NamedTuple', [])], fields=fields) return TupleType(list(fields.values()), fallback=Instance(namedtuple_info, []))
def visit_tuple_type(self, t: TupleType) -> Type: # Types such as (t1, t2, ...) only allowed in assignment statements. They'll # generate errors elsewhere, and Tuple[t1, t2, ...] must be used instead. if t.implicit and not self.allow_tuple_literal: self.fail('Invalid tuple literal type', t) if len(t.items) == 1: self.note_func('Suggestion: Is there a spurious trailing comma?', t) return AnyType(TypeOfAny.from_error) star_count = sum(1 for item in t.items if isinstance(item, StarType)) if star_count > 1: self.fail('At most one star type allowed in a tuple', t) if t.implicit: return TupleType([AnyType(TypeOfAny.from_error) for _ in t.items], self.named_type('builtins.tuple'), t.line) else: return AnyType(TypeOfAny.from_error) any_type = AnyType(TypeOfAny.special_form) fallback = t.fallback if t.fallback else self.named_type('builtins.tuple', [any_type]) return TupleType(self.anal_array(t.items), fallback, t.line)
def visit_tuple_type(self, t: TupleType) -> ProperType: # When given two fixed-length tuples: # * If they have the same length, join their subtypes item-wise: # Tuple[int, bool] + Tuple[bool, bool] becomes Tuple[int, bool] # # Otherwise, `t` is a fixed-length tuple but `self.s` is NOT: # * Joining with a variadic tuple returns variadic tuple: # Tuple[int, bool] + Tuple[bool, ...] becomes Tuple[int, ...] # * Joining with any Sequence also returns a Sequence: # Tuple[int, bool] + List[bool] becomes Sequence[int] if isinstance(self.s, TupleType) and self.s.length() == t.length(): items = [] # type: List[Type] for i in range(t.length()): items.append(self.join(t.items[i], self.s.items[i])) fallback = join_instances(mypy.typeops.tuple_fallback(self.s), mypy.typeops.tuple_fallback(t)) assert isinstance(fallback, Instance) return TupleType(items, fallback) else: return join_types(self.s, mypy.typeops.tuple_fallback(t))
def session_hook(ctx: MethodContext) -> Type: if not isinstance(ctx.default_return_type, Instance): return ctx.default_return_type if ctx.default_return_type.type.fullname( ) != 'typed_sqlalchemy.session.QueryPlaceholder': return ctx.default_return_type assert isinstance(ctx.type, Instance) assert ctx.type.type.fullname() == 'typed_sqlalchemy.session.Session' base_model_type = ctx.type.args[0] # We hijack these from the generic return value in order to construct them. query_type_info = ctx.default_return_type.args[0].type singular_query_type_info = ctx.default_return_type.args[1].type typed_column_type_info = ctx.default_return_type.args[2].type # For now, we support exactly two arg types: typed columns and models. # We record whether we found a model because if we only have one non-model # then we are still a tuple, but a single model becomes a singular query. has_model = False return_types = [] for arg_type in ctx.arg_types[0]: # I'm kind of surprised that its a CallableType if isinstance(arg_type, CallableType) and is_subtype( arg_type.ret_type, base_model_type): has_model = True return_types.append(arg_type.ret_type) # Case 2: TypedColumn elif isinstance(arg_type, Instance) and arg_type.type.fullname( ) == typed_column_type_info.fullname(): return_types.append(arg_type.args[1]) if has_model and len(return_types) == 1: model_type = return_types[0] primary_key_type = model_type.type.bases[0].args[0] return Instance( singular_query_type_info, # Base model, return type, primary key [base_model_type, return_types[0], primary_key_type], ) else: fallback = ctx.api.named_generic_type('builtins.tuple', []) return Instance( query_type_info, # Base model, return type tuple [base_model_type, TupleType(return_types, fallback)], )
def make_named_tuple(api: 'TypeChecker', fields: 'OrderedDict[str, Type]', name: str) -> Type: if not fields: # No fields specified, so fallback to a subclass of NamedTuple that allows # __getattr__ / __setattr__ for any attribute name. fallback = api.named_generic_type('django._NamedTupleAnyAttr', []) else: fallback = build_class_with_annotated_fields( api=api, base=api.named_generic_type('typing.NamedTuple', []), fields=fields, name=name ) return TupleType(list(fields.values()), fallback=fallback)
def visit_tuple_type(self, t: TupleType) -> Type: # Types such as (t1, t2, ...) only allowed in assignment statements. They'll # generate errors elsewhere, and Tuple[t1, t2, ...] must be used instead. if t.implicit and not self.allow_tuple_literal: self.fail(errorcode.INVALID_TUPLE_LITERAL_TYPE(), t) if len(t.items) == 1: self.note_func( errorcode.SUGGESTION_IS_THERE_SPURIOUS_TRAILING_COMMA(), t) return AnyType(TypeOfAny.from_error) star_count = sum(1 for item in t.items if isinstance(item, StarType)) if star_count > 1: self.fail(errorcode.AT_MOST_ONE_STAR_TYPE_ALLOWED_IN_TUPLE(), t) if t.implicit: return TupleType( [AnyType(TypeOfAny.from_error) for _ in t.items], self.named_type('builtins.tuple'), t.line) else: return AnyType(TypeOfAny.from_error) any_type = AnyType(TypeOfAny.special_form) fallback = t.fallback if t.fallback else self.named_type( 'builtins.tuple', [any_type]) return TupleType(self.anal_array(t.items), fallback, t.line)
def analyze_type_with_type_info(self, info: TypeInfo, args: List[Type], ctx: Context) -> Type: """Bind unbound type when were able to find target TypeInfo. This handles simple cases like 'int', 'modname.UserClass[str]', etc. """ if len(args) > 0 and info.fullname() == 'builtins.tuple': fallback = Instance(info, [AnyType(TypeOfAny.special_form)], ctx.line) return TupleType(self.anal_array(args), fallback, ctx.line) # Analyze arguments and (usually) construct Instance type. The # number of type arguments and their values are # checked only later, since we do not always know the # valid count at this point. Thus we may construct an # Instance with an invalid number of type arguments. instance = Instance(info, self.anal_array(args), ctx.line, ctx.column) # Check type argument count. if len(instance.args) != len(info.type_vars) and not self.defining_alias: fix_instance(instance, self.fail) if not args and self.options.disallow_any_generics and not self.defining_alias: # We report/patch invalid built-in instances already during second pass. # This is done to avoid storing additional state on instances. # All other (including user defined) generics will be patched/reported # in the third pass. if not self.is_typeshed_stub and info.fullname() in nongen_builtins: alternative = nongen_builtins[info.fullname()] self.fail(message_registry.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative), ctx) any_type = AnyType(TypeOfAny.from_error, line=ctx.line) else: any_type = AnyType(TypeOfAny.from_omitted_generics, line=ctx.line) instance.args = [any_type] * len(info.type_vars) tup = info.tuple_type if tup is not None: # The class has a Tuple[...] base class so it will be # represented as a tuple type. if args: self.fail('Generic tuple types not supported', ctx) return AnyType(TypeOfAny.from_error) return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) td = info.typeddict_type if td is not None: # The class has a TypedDict[...] base class so it will be # represented as a typeddict type. if args: self.fail('Generic TypedDict types not supported', ctx) return AnyType(TypeOfAny.from_error) # Create a named TypedDictType return td.copy_modified(item_types=self.anal_array(list(td.items.values())), fallback=instance) return instance
def make_oneoff_named_tuple( api: TypeChecker, name: str, fields: "OrderedDict[str, MypyType]", extra_bases: Optional[List[Instance]] = None) -> TupleType: current_module = get_current_module(api) if extra_bases is None: extra_bases = [] namedtuple_info = add_new_class_for_module( current_module, name, bases=[api.named_generic_type("typing.NamedTuple", [])] + extra_bases, fields=fields) return TupleType(list(fields.values()), fallback=Instance(namedtuple_info, []))
def parse_types(self) -> Type: parens = False if self.current_token_str() == '(': self.skip() parens = True type = self.parse_type() if self.current_token_str() == ',': items = [type] while self.current_token_str() == ',': self.skip() items.append(self.parse_type()) type = TupleType(items) if parens: self.expect(')') return type
def visit_tuple_type(self, t: TupleType) -> Type: # Types such as (t1, t2, ...) only allowed in assignment statements. They'll # generate errors elsewhere, and Tuple[t1, t2, ...] must be used instead. if t.implicit and not self.allow_tuple_literal: self.fail('Syntax error in type annotation', t) if len(t.items) == 1: self.note_func('Suggestion: Is there a spurious trailing comma?', t) else: self.note_func('Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn)', t) return AnyType(TypeOfAny.from_error) star_count = sum(1 for item in t.items if isinstance(item, StarType)) if star_count > 1: self.fail('At most one star type allowed in a tuple', t) if t.implicit: return TupleType([AnyType(TypeOfAny.from_error) for _ in t.items], self.named_type('builtins.tuple'), t.line) else: return AnyType(TypeOfAny.from_error) any_type = AnyType(TypeOfAny.special_form) # If the fallback isn't filled in yet, its type will be the falsey FakeInfo fallback = (t.partial_fallback if t.partial_fallback.type else self.named_type('builtins.tuple', [any_type])) return TupleType(self.anal_array(t.items), fallback, t.line)
def session_query_hook(ctx: MethodContext) -> Type: """ Turns session.query(...) into typed Query. Examples: session.query(User) -> Query[User] session.query(User, Order) -> Query[Tuple[User, Order]] session.query(User, User.id) -> Query[Tuple[User, int]] session.query(User.id) -> Query[Tuple[int]] """ # TODO take a look at Session.query_cls? Do we have to do this with generics? cnt_entities = 0 def map_arg(arg: Type) -> Type: nonlocal cnt_entities # A model class (well, in this case a callable constructor) # Example: # session.query(Employee) -> Query[Employee] if isinstance(arg, CallableType) and arg.is_type_obj(): cnt_entities += 1 return arg.ret_type # Example: # session.query(Employee.id, ...) -> Query[int, ...] if isinstance(arg, Instance) and fullname(arg.type) == COLUMN_NAME: assert (len(arg.args) == 1 ), "Column[...] should have only one generic argument" return arg.args[0] return AnyType(TypeOfAny.implementation_artifact) # take positional arguments and map them args = [map_arg(arg) for arg in ctx.arg_types[0]] if len(args) == 1 and (cnt_entities == 1 or isinstance(args[0], AnyType)): # when there is a single class passed as an argument # or when we can't detect what the single argument is (Any) final_arg = args[0] else: fallback = ctx.api.named_type( "sqlalchemy.util.KeyedTuple") # type: ignore final_arg = TupleType(args, fallback, implicit=True) return ctx.api.named_generic_type("sqlalchemy.orm.Query", [final_arg])
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, )
def parse_types(self) -> Type: """ Parse either a single type or a comma separated list of types as a tuple type. In the latter case, a trailing comma is needed when the list contains only a single type (and optional otherwise). int -> int int, -> TupleType[int] int, int, int -> TupleType[int, int, int] """ type = self.parse_type() if self.current_token_str() == ',': items = [type] while self.current_token_str() == ',': self.skip() if self.current_token_str() == ')': break items.append(self.parse_type()) type = TupleType(items, None, type.line, implicit=True) return type
def _add_attrs_magic_attribute(ctx: 'mypy.plugin.ClassDefContext', raw_attr_types: 'List[Optional[Type]]') -> None: attr_name = '__attrs_attrs__' any_type = AnyType(TypeOfAny.explicit) attributes_types: 'List[Type]' = [ ctx.api.named_type_or_none('attr.Attribute', [attr_type or any_type]) or any_type for attr_type in raw_attr_types ] fallback_type = ctx.api.named_type('builtins.tuple', [ ctx.api.named_type_or_none('attr.Attribute', [any_type]) or any_type, ]) var = Var(name=attr_name, type=TupleType(attributes_types, fallback=fallback_type)) var.info = ctx.cls.info var._fullname = ctx.cls.info.fullname + '.' + attr_name ctx.cls.info.names[attr_name] = SymbolTableNode( kind=MDEF, node=var, plugin_generated=True, )
def session_query_hook(ctx: MethodContext) -> Type: """ Turns session.query(...) into typed Query. Examples: session.query(User) -> Query[User] session.query(User, Order) -> Query[Tuple[User, Order]] session.query(User, User.id) -> Query[Tuple[User, int]] """ # TODO take a look at Session.query_cls? Do we have to do this with generics? def map_arg(arg: Type) -> Type: # A model class (well, in this case a callable constructor) # Example: # session.query(Employee) -> Query[Employee] if isinstance(arg, CallableType) and arg.is_type_obj(): return arg.ret_type # Example: # session.query(Employee.id) -> Query[int] if isinstance(arg, Instance) and arg.type.fullname() == COLUMN_NAME: assert len( arg.args ) == 1, "Column[...] should have only one generic argument" return arg.args[0] return AnyType(TypeOfAny.implementation_artifact) # take positional arguments and map them args = [map_arg(arg) for arg in ctx.arg_types[0]] if len(args) == 1: final_arg = args[0] else: fallback = ctx.api.named_type( 'sqlalchemy.util._collections.AbstractKeyedTuple') final_arg = TupleType(args, fallback, implicit=True) return ctx.default_return_type.copy_modified(args=[final_arg])
def visit_instance(self, t: Instance, from_fallback: bool = False) -> Type: """This visitor method tracks situations like this: x: A # When analyzing this type we will get an Instance from SemanticAnalyzerPass1. # Now we need to update this to actual analyzed TupleType. class A(NamedTuple): attr: str If from_fallback is True, then we always return an Instance type. This is needed since TupleType and TypedDictType fallbacks are always instances. """ info = t.type # Special case, analyzed bases transformed the type into TupleType. if info.tuple_type and not from_fallback: items = [it.accept(self) for it in info.tuple_type.items] info.tuple_type.items = items return TupleType(items, Instance(info, [])) # Update forward Instances to corresponding analyzed NamedTuples. if info.replaced and info.replaced.tuple_type: tp = info.replaced.tuple_type if self.check_recursion(tp): # The key idea is that when we recursively return to a type already traversed, # then we break the cycle and put AnyType as a leaf. return AnyType(TypeOfAny.from_error) return tp.copy_modified( fallback=Instance(info.replaced, [], line=t.line)).accept(self) # Same as above but for TypedDicts. if info.replaced and info.replaced.typeddict_type: td = info.replaced.typeddict_type if self.check_recursion(td): # We also break the cycles for TypedDicts as explained above for NamedTuples. return AnyType(TypeOfAny.from_error) return td.copy_modified( fallback=Instance(info.replaced, [], line=t.line)).accept(self) if self.check_recursion(t): # We also need to break a potential cycle with normal (non-synthetic) instance types. return Instance(t.type, [AnyType(TypeOfAny.from_error)] * len(t.type.defn.type_vars), line=t.line) return super().visit_instance(t)
def visit_tuple_type(self, t: TupleType) -> Type: items = [] for item in t.items: proper_item = get_proper_type(item) if isinstance(proper_item, UnpackType): unpacked_items = self.expand_unpack(proper_item) if unpacked_items is None: # TODO: better error, something like tuple of unknown? return UninhabitedType() elif isinstance(unpacked_items, Instance): if len(t.items) == 1: return unpacked_items else: assert False, "Invalid unpack of variable length tuple" elif isinstance(unpacked_items, AnyType): return unpacked_items else: items.extend(unpacked_items) else: items.append(proper_item.accept(self)) return t.copy_modified(items=items)
def mutate_typeclass_instance_def( instance: Instance, *, passed_types: List[MypyType], typeclass: Instance, ctx: Union[MethodContext, FunctionContext], ) -> None: """ Mutates ``TypeClassInstanceDef`` args. That's where we fill their values. Why? Because we need all types from ``some.instance()`` call. Including ``is_protocol`` for later checks. """ tuple_type = TupleType( # We now store passed arg types in a single tuple: passed_types, fallback=ctx.api.named_type('builtins.tuple'), # type: ignore ) instance.args = ( tuple_type, # Passed runtime types, like str in `@some.instance(str)` typeclass, # `_TypeClass` instance itself )
def _add_attrs_magic_attribute( ctx: 'mypy.plugin.ClassDefContext', attrs: 'List[Tuple[str, Optional[Type]]]') -> None: any_type = AnyType(TypeOfAny.explicit) attributes_types: 'List[Type]' = [ ctx.api.named_type_or_none('attr.Attribute', [attr_type or any_type]) or any_type for _, attr_type in attrs ] fallback_type = ctx.api.named_type('builtins.tuple', [ ctx.api.named_type_or_none('attr.Attribute', [any_type]) or any_type, ]) ti = ctx.api.basic_new_typeinfo(MAGIC_ATTR_CLS_NAME, fallback_type, 0) ti.is_named_tuple = True for (name, _), attr_type in zip(attrs, attributes_types): var = Var(name, attr_type) var.is_property = True proper_type = get_proper_type(attr_type) if isinstance(proper_type, Instance): var.info = proper_type.type ti.names[name] = SymbolTableNode(MDEF, var, plugin_generated=True) attributes_type = Instance(ti, []) # TODO: refactor using `add_attribute_to_class` var = Var(name=MAGIC_ATTR_NAME, type=TupleType(attributes_types, fallback=attributes_type)) var.info = ctx.cls.info var.is_classvar = True var._fullname = f"{ctx.cls.fullname}.{MAGIC_ATTR_CLS_NAME}" var.allow_incompatible_override = True ctx.cls.info.names[MAGIC_ATTR_NAME] = SymbolTableNode( kind=MDEF, node=var, plugin_generated=True, no_serialize=True, )
def tuple_type(self, items: List[Type]) -> TupleType: any_type = AnyType(TypeOfAny.special_form) return TupleType(items, fallback=self.named_type('builtins.tuple', [any_type]))
def visit_unbound_type(self, t: UnboundType) -> Type: if t.optional: t.optional = False # We don't need to worry about double-wrapping Optionals or # wrapping Anys: Union simplification will take care of that. return make_optional_type(self.visit_unbound_type(t)) sym = self.lookup(t.name, t, suppress_errors=self.third_pass) # type: ignore if sym is not None: if sym.node is None: # UNBOUND_IMPORTED can happen if an unknown name was imported. if sym.kind != UNBOUND_IMPORTED: self.fail(errorcode.INTERNAL_ERROR_NODE_IS_NONE(sym.kind), t) return AnyType(TypeOfAny.special_form) fullname = sym.node.fullname() hook = self.plugin.get_type_analyze_hook(fullname) if hook: return hook(AnalyzeTypeContext(t, t, self)) if (fullname in nongen_builtins and t.args and not sym.normalized and not self.allow_unnormalized): self.fail(errorcode.NO_SUBSCRIPT_BUILTIN_ALIAS(fullname), t) if self.tvar_scope: tvar_def = self.tvar_scope.get_binding(sym) else: tvar_def = None if self.warn_bound_tvar and sym.kind == TVAR and tvar_def is not None: self.fail( errorcode. CANNOT_USE_BOUND_TYPE_VAR_TO_DEFINE_GENERIC_ALIAS(t.name), t) return AnyType(TypeOfAny.from_error) elif sym.kind == TVAR and tvar_def is not None: if len(t.args) > 0: self.fail(errorcode.TYPE_VAR_USED_WITH_ARG(t.name), t) return TypeVarType(tvar_def, t.line) elif fullname == 'builtins.None': return NoneTyp() elif fullname == 'typing.Any' or fullname == 'builtins.Any': return AnyType(TypeOfAny.explicit) elif fullname == 'typing.Tuple': if len(t.args) == 0 and not t.empty_tuple_index: # Bare 'Tuple' is same as 'tuple' if self.options.disallow_any_generics and not self.is_typeshed_stub: self.fail(errorcode.BARE_GENERIC(), t) typ = self.named_type('builtins.tuple', line=t.line, column=t.column) typ.from_generic_builtin = True return typ if len(t.args) == 2 and isinstance(t.args[1], EllipsisType): # Tuple[T, ...] (uniform, variable-length tuple) instance = self.named_type('builtins.tuple', [self.anal_type(t.args[0])]) instance.line = t.line return instance return self.tuple_type(self.anal_array(t.args)) elif fullname == 'typing.Union': items = self.anal_array(t.args) return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: self.fail(errorcode.OPTIONAL_MUST_HAVE_ONE_ARGUMENT(), t) return AnyType(TypeOfAny.from_error) item = self.anal_type(t.args[0]) return make_optional_type(item) elif fullname == 'typing.Callable': return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: any_type = AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) return TypeType(any_type, line=t.line, column=t.column) if len(t.args) != 1: self.fail(errorcode.TYPE_MUST_HAVE_EXACTLY_ONE_TYPE(), t) item = self.anal_type(t.args[0]) return TypeType.make_normalized(item, line=t.line) elif fullname == 'typing.ClassVar': if self.nesting_level > 0: self.fail( errorcode.INVALID_TYPE_CLASSVAR_NESTED_INSIDE_TYPE(), t) if len(t.args) == 0: return AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) if len(t.args) != 1: self.fail( errorcode.CLASS_VAR_MUST_HAVE_AT_MOST_ONE_TYPE_ARG(), t) return AnyType(TypeOfAny.from_error) item = self.anal_type(t.args[0]) if isinstance(item, TypeVarType) or get_type_vars(item): self.fail(errorcode.CLASSVAR_CANNOT_BE_GENERIC(), t) return AnyType(TypeOfAny.from_error) return item elif fullname in ('mypy_extensions.NoReturn', 'typing.NoReturn'): return UninhabitedType(is_noreturn=True) elif sym.kind == TYPE_ALIAS: override = sym.type_override all_vars = sym.alias_tvars assert override is not None an_args = self.anal_array(t.args) if all_vars is not None: exp_len = len(all_vars) else: exp_len = 0 act_len = len(an_args) if exp_len > 0 and act_len == 0: # Interpret bare Alias same as normal generic, i.e., Alias[Any, Any, ...] assert all_vars is not None return set_any_tvars(override, all_vars, t.line, t.column) if exp_len == 0 and act_len == 0: return override if act_len != exp_len: self.fail( errorcode.BAD_NUMBER_ARGUMENT_FOR_TYPEALIAS( exp_len, act_len), t) return set_any_tvars(override, all_vars or [], t.line, t.column, implicit=False) assert all_vars is not None return replace_alias_tvars(override, all_vars, an_args, t.line, t.column) elif not isinstance(sym.node, TypeInfo): name = sym.fullname if name is None: name = sym.node.name() if isinstance(sym.node, Var) and isinstance( sym.node.type, AnyType): # 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. return AnyType(TypeOfAny.from_unimported_type) # Allow unbound type variables when defining an alias if not (self.aliasing and sym.kind == TVAR and (not self.tvar_scope or self.tvar_scope.get_binding(sym) is None)): if (not self.third_pass and not self.in_dynamic_func and not (isinstance(sym.node, (FuncDef, Decorator)) or isinstance(sym.node, Var) and sym.node.is_ready) and not (sym.kind == TVAR and tvar_def is None)): if t.args and not self.global_scope: self.fail( errorcode.UNSUPPORTED_FORWARD_REFERENCE( t.name), t) return AnyType(TypeOfAny.from_error) return ForwardRef(t) self.fail(errorcode.INVALID_TYPE_X(name), t) if self.third_pass and sym.kind == TVAR: self.note_func( errorcode.FORWARD_REERENCES_TO_TYPE_VAR_PROHIBITED( ), t) return t info = sym.node # type: TypeInfo if len(t.args) > 0 and info.fullname() == 'builtins.tuple': fallback = Instance(info, [AnyType(TypeOfAny.special_form)], t.line) return TupleType(self.anal_array(t.args), fallback, t.line) else: # Analyze arguments and construct Instance type. The # number of type arguments and their values are # checked only later, since we do not always know the # valid count at this point. Thus we may construct an # Instance with an invalid number of type arguments. instance = Instance(info, self.anal_array(t.args), t.line, t.column) instance.from_generic_builtin = sym.normalized tup = info.tuple_type if tup is not None: # The class has a Tuple[...] base class so it will be # represented as a tuple type. if t.args: self.fail( errorcode.GENERIC_TUPLE_TYPES_NOT_SUPPORTED(), t) return AnyType(TypeOfAny.from_error) return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) td = info.typeddict_type if td is not None: # The class has a TypedDict[...] base class so it will be # represented as a typeddict type. if t.args: self.fail( errorcode.GENERIC_TYPEDDICT_TYPES_NOT_SUPPORTED(), t) return AnyType(TypeOfAny.from_error) # Create a named TypedDictType return td.copy_modified(item_types=self.anal_array( list(td.items.values())), fallback=instance) return instance else: if self.third_pass: self.fail(errorcode.INVALID_TYPE_X(t.name), t) return AnyType(TypeOfAny.from_error) return AnyType(TypeOfAny.special_form)
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') info = self.api.basic_new_typeinfo(name, fallback) info.is_named_tuple = True tuple_base = TupleType(types, fallback) info.tuple_type = tuple_base info.line = line # 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) tvd = TypeVarDef(SELF_TVAR_NAME, info.fullname + '.' + SELF_TVAR_NAME, -1, [], info.tuple_type) selftype = TypeVarType(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
def visit_Tuple(self, n: ast35.Tuple) -> Type: return TupleType(self.visit_list(n.elts), None, implicit=True, line=self.line)
def make_tuple(api: "TypeChecker", fields: List[MypyType]) -> TupleType: # fallback for tuples is any builtins.tuple instance fallback = api.named_generic_type("builtins.tuple", [AnyType(TypeOfAny.special_form)]) return TupleType(fields, fallback=fallback)
def visit_tuple_type(self, t: TupleType) -> Type: return t.copy_modified(items=self.expand_types(t.items))