def test_last_known_values_with_merge(self) -> None: t = UnionType.make_union( [self.fx.lit1_inst, self.fx.lit2_inst, self.fx.lit4_inst]) assert remove_instance_last_known_values(t) == self.fx.a t = UnionType.make_union([ self.fx.lit1_inst, self.fx.b, self.fx.lit2_inst, self.fx.lit4_inst ]) self.assert_union_result(t, [self.fx.a, self.fx.b])
def test_multiple_same_instances(self) -> None: t = UnionType.make_union([self.fx.a, self.fx.a]) assert remove_instance_last_known_values(t) == self.fx.a t = UnionType.make_union([self.fx.a, self.fx.a, self.fx.b]) self.assert_union_result(t, [self.fx.a, self.fx.b]) t = UnionType.make_union( [self.fx.a, self.fx.nonet, self.fx.a, self.fx.b]) self.assert_union_result(t, [self.fx.a, self.fx.nonet, self.fx.b])
def restrict_subtype_away(t: Type, s: Type) -> Type: """Return t minus s. If we can't determine a precise result, return a supertype of the ideal result (just t is a valid result). This is used for type inference of runtime type checks such as isinstance. Currently this just removes elements of a union type. """ if isinstance(t, UnionType): # Since runtime type checks will ignore type arguments, erase the types. erased_s = erase_type(s) # TODO: Implement more robust support for runtime isinstance() checks, # see issue #3827 new_items = [ item for item in t.relevant_items() if (not (is_proper_subtype(erase_type(item), erased_s) or is_proper_subtype(item, erased_s)) or isinstance(item, AnyType)) ] return UnionType.make_union(new_items) else: return t
def visit_union_type(self, t: UnionType) -> Type: new = cast(UnionType, super().visit_union_type(t)) # Erasure can result in many duplicate items; merge them. # Call make_simplified_union only on lists of instance types # that all have the same fullname, to avoid simplifying too # much. instances = [item for item in new.items if isinstance(get_proper_type(item), Instance)] # Avoid merge in simple cases such as optional types. if len(instances) > 1: instances_by_name: Dict[str, List[Instance]] = {} new_items = get_proper_types(new.items) for item in new_items: if isinstance(item, Instance) and not item.args: instances_by_name.setdefault(item.type.fullname, []).append(item) merged: List[Type] = [] for item in new_items: if isinstance(item, Instance) and not item.args: types = instances_by_name.get(item.type.fullname) if types is not None: if len(types) == 1: merged.append(item) else: from mypy.typeops import make_simplified_union merged.append(make_simplified_union(types)) del instances_by_name[item.type.fullname] else: merged.append(item) return UnionType.make_union(merged) return new
def get_args(self, is_method: bool, base: CallableType, defaults: List[Optional[Type]], callsites: List[Callsite]) -> List[List[Type]]: """Produce a list of type suggestions for each argument type.""" types = [] # type: List[List[Type]] for i in range(len(base.arg_kinds)): # Make self args Any but this will get overriden somewhere in the checker # We call this a special form so that has_any_type doesn't consider it to be a real any if i == 0 and is_method: types.append([AnyType(TypeOfAny.special_form)]) continue all_arg_types = [] for call in callsites: for typ in call.arg_types[i - is_method]: # Collect all the types except for implicit anys if not is_implicit_any(typ): all_arg_types.append(typ) # Add in any default argument types default = defaults[i] if default: all_arg_types.append(default) if len(all_arg_types) == 1 and isinstance(get_proper_type(all_arg_types[0]), NoneType): types.append( [UnionType.make_union([all_arg_types[0], AnyType(TypeOfAny.explicit)])]) elif all_arg_types: types.append(generate_type_combinations(all_arg_types)) else: # If we don't have anything, we'll try Any and object # (Actually object usually is bad for downstream consumers...) # types.append([AnyType(TypeOfAny.explicit), self.builtin_type('builtins.object')]) types.append([AnyType(TypeOfAny.explicit)]) return types
def get_field_lookup_exact_type(self, api: TypeChecker, field: Field) -> MypyType: if isinstance(field, (RelatedField, ForeignObjectRel)): related_model_cls = field.related_model primary_key_field = self.get_primary_key_field(related_model_cls) primary_key_type = self.get_field_get_type(api, primary_key_field, method='init') rel_model_info = helpers.lookup_class_typeinfo( api, related_model_cls) if rel_model_info is None: return AnyType(TypeOfAny.explicit) model_and_primary_key_type = UnionType.make_union( [Instance(rel_model_info, []), primary_key_type]) return helpers.make_optional(model_and_primary_key_type) # return helpers.make_optional(Instance(rel_model_info, [])) field_info = helpers.lookup_class_typeinfo(api, field.__class__) if field_info is None: return AnyType(TypeOfAny.explicit) return helpers.get_private_descriptor_type(field_info, '_pyi_lookup_exact_type', is_nullable=field.null)
def restrict_subtype_away(t: Type, s: Type) -> Type: """Return a supertype of (t intersect not s) Currently just remove elements of a union type. """ if isinstance(t, UnionType): new_items = [item for item in t.items if not is_subtype(item, s)] return UnionType.make_union(new_items) else: return t
def proper_types_hook(ctx: FunctionContext) -> Type: """Check if this get_proper_types() call is not redundant.""" arg_types = ctx.arg_types[0] if arg_types: arg_type = arg_types[0] proper_type = get_proper_type_instance(ctx) item_type = UnionType.make_union([NoneTyp(), proper_type]) ok_type = ctx.api.named_generic_type('typing.Iterable', [item_type]) if is_proper_subtype(arg_type, ok_type): ctx.api.fail('Redundant call to get_proper_types()', ctx.context) return ctx.default_return_type
def make_simplified_union(items: Sequence[Type], line: int = -1, column: int = -1, *, keep_erased: bool = False) -> ProperType: """Build union type with redundant union items removed. If only a single item remains, this may return a non-union type. Examples: * [int, str] -> Union[int, str] * [int, object] -> object * [int, int] -> int * [int, Any] -> Union[int, Any] (Any types are not simplified away!) * [Any, Any] -> Any Note: This must NOT be used during semantic analysis, since TypeInfos may not be fully initialized. The keep_erased flag is used for type inference against union types containing type variables. If set to True, keep all ErasedType items. """ items = get_proper_types(items) while any(isinstance(typ, UnionType) for typ in items): all_items = [] # type: List[ProperType] for typ in items: if isinstance(typ, UnionType): all_items.extend(get_proper_types(typ.items)) else: all_items.append(typ) items = all_items from mypy.subtypes import is_proper_subtype removed = set() # type: Set[int] for i, ti in enumerate(items): if i in removed: continue # Keep track of the truishness info for deleted subtypes which can be relevant cbt = cbf = False for j, tj in enumerate(items): if i != j and is_proper_subtype( tj, ti, keep_erased_types=keep_erased): # We found a redundant item in the union. removed.add(j) cbt = cbt or tj.can_be_true cbf = cbf or tj.can_be_false # if deleted subtypes had more general truthiness, use that if not ti.can_be_true and cbt: items[i] = true_or_false(ti) elif not ti.can_be_false and cbf: items[i] = true_or_false(ti) simplified_set = [items[i] for i in range(len(items)) if i not in removed] return UnionType.make_union(simplified_set, line, column)
def _adjust_typeclass_type(self, ctx, instance_type): unified = list(filter( # It means that function was defined without annotation # or with explicit `Any`, we prevent our Union from polution. # Because `Union[Any, int]` is just `Any`. # We also clear accidential type vars. self._filter_out_unified_types, [instance_type, ctx.type.args[0]], )) if not isinstance(instance_type, TypeVarType): ctx.type.args[0] = UnionType.make_union(unified)
def proper_type_hook(ctx: FunctionContext) -> Type: """Check if this get_proper_type() call is not redundant.""" arg_types = ctx.arg_types[0] if arg_types: arg_type = get_proper_type(arg_types[0]) proper_type = get_proper_type_instance(ctx) if is_proper_subtype(arg_type, UnionType.make_union([NoneTyp(), proper_type])): # Minimize amount of spurious errors from overload machinery. # TODO: call the hook on the overload as a whole? if isinstance(arg_type, (UnionType, Instance)): ctx.api.fail('Redundant call to get_proper_type()', ctx.context) return ctx.default_return_type
def merge_with_any(constraint: Constraint) -> Constraint: """Transform a constraint target into a union with given Any type.""" target = constraint.target if is_union_with_any(target): # Do not produce redundant unions. return constraint # TODO: if we will support multiple sources Any, use this here instead. any_type = AnyType(TypeOfAny.implementation_artifact) return Constraint( constraint.type_var, constraint.op, UnionType.make_union([target, any_type], target.line, target.column), )
def is_basic_index_sequence(type: Type): # From: https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html # >> Basic slicing occurs when obj is a slice object # >> an integer, o a tuple of slice objects and integers. # >> Ellipsis and newaxis objects can be interspersed with these as well. u = UnionType.make_union([ int_type(), API.named_type('builtins.slice'), NoneTyp(), API.named_type('builtins.ellipsis'), ]) return is_subtype(type, API.named_generic_type('typing.Sequence', args=[u]))
def visit_value_pattern(self, o: ValuePattern) -> PatternType: current_type = self.type_context[-1] typ = self.chk.expr_checker.accept(o.expr) typ = coerce_to_literal(typ) narrowed_type, rest_type = self.chk.conditional_types_with_intersection( current_type, [get_type_range(typ)], o, default=current_type ) if not isinstance(get_proper_type(narrowed_type), (LiteralType, UninhabitedType)): return PatternType(narrowed_type, UnionType.make_union([narrowed_type, rest_type]), {}) return PatternType(narrowed_type, rest_type, {})
def analyze_literal_type(self, t: UnboundType) -> Type: if len(t.args) == 0: self.fail('Literal[...] must have at least one parameter', t) return AnyType(TypeOfAny.from_error) output = [] # type: List[Type] for i, arg in enumerate(t.args): analyzed_types = self.analyze_literal_param(i + 1, arg, t) if analyzed_types is None: return AnyType(TypeOfAny.from_error) else: output.extend(analyzed_types) return UnionType.make_union(output, line=t.line)
def transform_oas_type( oas_type: oas_model.OASType[Any], handler_type: ProperType, ctx: FunctionContext, ) -> Type: m_type: Optional[Type] = None union_members: List[Type] = [] assert isinstance(ctx.api, TypeChecker) if isinstance(oas_type, oas_model.OASStringType): m_type = ctx.api.str_type() elif isinstance(oas_type, oas_model.OASBooleanType): m_type = ctx.api.named_type('bool') elif isinstance(oas_type, oas_model.OASNumberType): m_type = ctx.api.named_type( 'int' if issubclass(int, oas_type.number_cls) else 'float', ) elif isinstance(oas_type, oas_model.OASOneOfType): union_members = [ transform_oas_type( nested_type, handler_type, ctx, ) for include_type, nested_type in oas_type.schemas if include_type ] elif isinstance(oas_type, oas_model.OASArrayType): m_type = ctx.api.named_generic_type( name='set' if oas_type.unique_items else 'list', args=[ transform_oas_type( oas_type.items_type, handler_type, ctx, ) ], ) elif isinstance(oas_type, oas_model.OASObjectType): m_type = transform_oas_object_type(oas_type, handler_type, ctx) else: raise NotImplementedError(f'{oas_type} not yet implemented') if m_type is not None: m_type.set_line(handler_type) union_members.append(m_type) if oas_type.nullable: union_members.append(NoneType()) ut = simplify_union(UnionType.make_union(items=union_members)) ut.set_line(handler_type) return ut
def _combine_hook(context: FunctionContext): result_types = [] error_types = [] env_types = [] try: for effect_type in context.arg_types[0]: env_type, error_type, result_type = get_proper_type( effect_type).args env_types.append(env_type) error_types.append(error_type) result_types.append(result_type) map_return_type_def = _type_var_def( 'R1', 'pfun.effect', context.api.named_type('builtins.object')) map_return_type = TypeVarType(map_return_type_def) map_function_type = CallableType( arg_types=result_types, arg_kinds=[ARG_POS for _ in result_types], arg_names=[None for _ in result_types], ret_type=map_return_type, variables=[map_return_type_def], fallback=context.api.named_type('builtins.function')) ret_type = context.default_return_type.ret_type combined_error_type = UnionType.make_union( sorted(set(error_types), key=str)) ret_type_args = ret_type.args ret_type_args[1] = combined_error_type ret_type_args[2] = map_return_type env_types = [ env_type for env_type in env_types if not isinstance(env_type, AnyType) ] if len(set(env_types)) == 1: combined_env_type = env_types[0] elif env_types and all( hasattr(env_type, 'type') and env_type.type.is_protocol for env_type in env_types): combined_env_type = reduce(_combine_protocols, env_types) else: combined_env_type = ret_type_args[0] ret_type_args[0] = combined_env_type ret_type = ret_type.copy_modified(args=ret_type_args) return CallableType( arg_types=[map_function_type], arg_kinds=[ARG_POS], arg_names=[None], variables=[map_return_type_def], ret_type=ret_type, fallback=context.api.named_type('builtins.function')) except AttributeError: return context.default_return_type
def conversion_type(self, p: str, context: Context, expr: FormatStringExpr, format_call: bool = False) -> Optional[Type]: """Return the type that is accepted for a string interpolation conversion specifier type. Note that both Python's float (e.g. %f) and integer (e.g. %d) specifier types accept both float and integers. The 'format_call' argument indicates whether this type came from % interpolation or from a str.format() call, the meaning of few formatting types are different. """ NUMERIC_TYPES = NUMERIC_TYPES_NEW if format_call else NUMERIC_TYPES_OLD INT_TYPES = REQUIRE_INT_NEW if format_call else REQUIRE_INT_OLD if p == 'b' and not format_call: if self.chk.options.python_version < (3, 5): self.msg.fail("Format character 'b' is only supported in Python 3.5 and later", context, code=codes.STRING_FORMATTING) return None if not isinstance(expr, BytesExpr): self.msg.fail("Format character 'b' is only supported on bytes patterns", context, code=codes.STRING_FORMATTING) return None return self.named_type('builtins.bytes') elif p == 'a': if self.chk.options.python_version < (3, 0): self.msg.fail("Format character 'a' is only supported in Python 3", context, code=codes.STRING_FORMATTING) return None # TODO: return type object? return AnyType(TypeOfAny.special_form) elif p in ['s', 'r']: return AnyType(TypeOfAny.special_form) elif p in NUMERIC_TYPES: if p in INT_TYPES: numeric_types = [self.named_type('builtins.int')] else: numeric_types = [self.named_type('builtins.int'), self.named_type('builtins.float')] if not format_call: if p in FLOAT_TYPES: numeric_types.append(self.named_type('typing.SupportsFloat')) else: numeric_types.append(self.named_type('typing.SupportsInt')) return UnionType.make_union(numeric_types) elif p in ['c']: return UnionType([self.named_type('builtins.int'), self.named_type('builtins.float'), self.named_type('builtins.str')]) else: self.msg.unsupported_placeholder(p, context) return None
def get_args(self, is_method: bool, base: CallableType, defaults: List[Optional[Type]], callsites: List[Callsite], uses: List[List[Type]]) -> List[List[Type]]: """Produce a list of type suggestions for each argument type.""" types = [] # type: List[List[Type]] for i in range(len(base.arg_kinds)): # Make self args Any but this will get overriden somewhere in the checker # We call this a special form so that has_any_type doesn't consider it to be a real any if i == 0 and is_method: types.append([AnyType(TypeOfAny.special_form)]) continue all_arg_types = [] for call in callsites: for typ in call.arg_types[i - is_method]: # Collect all the types except for implicit anys if not is_implicit_any(typ): all_arg_types.append(typ) all_use_types = [] for typ in uses[i]: # Collect all the types except for implicit anys if not is_implicit_any(typ): all_use_types.append(typ) # Add in any default argument types default = defaults[i] if default: all_arg_types.append(default) if all_use_types: all_use_types.append(default) arg_types = [] if (all_arg_types and all( isinstance(get_proper_type(tp), NoneType) for tp in all_arg_types)): arg_types.append( UnionType.make_union( [all_arg_types[0], AnyType(TypeOfAny.explicit)])) elif all_arg_types: arg_types.extend(generate_type_combinations(all_arg_types)) else: arg_types.append(AnyType(TypeOfAny.explicit)) if all_use_types: # This is a meet because the type needs to be compatible with all the uses arg_types.append(meet_type_list(all_use_types)) types.append(arg_types) return types
def make_simplified_union(items: Sequence[Type], line: int = -1, column: int = -1, *, keep_erased: bool = False, contract_literals: bool = True) -> ProperType: """Build union type with redundant union items removed. If only a single item remains, this may return a non-union type. Examples: * [int, str] -> Union[int, str] * [int, object] -> object * [int, int] -> int * [int, Any] -> Union[int, Any] (Any types are not simplified away!) * [Any, Any] -> Any Note: This must NOT be used during semantic analysis, since TypeInfos may not be fully initialized. The keep_erased flag is used for type inference against union types containing type variables. If set to True, keep all ErasedType items. The contract_literals flag indicates whether we need to contract literal types back into a sum type. Set it to False when called by try_expanding_sum_type_ to_union(). """ items = get_proper_types(items) # Step 1: expand all nested unions while any(isinstance(typ, UnionType) for typ in items): all_items: List[ProperType] = [] for typ in items: if isinstance(typ, UnionType): all_items.extend(get_proper_types(typ.items)) else: all_items.append(typ) items = all_items # Step 2: remove redundant unions simplified_set = _remove_redundant_union_items(items, keep_erased) # Step 3: If more than one literal exists in the union, try to simplify if contract_literals and sum( isinstance(item, LiteralType) for item in simplified_set) > 1: simplified_set = try_contracting_literals_in_union(simplified_set) return UnionType.make_union(simplified_set, line, column)
def coerce_to_literal(typ: Type) -> Type: """Recursively converts any Instances that have a last_known_value or are instances of enum types with a single value into the corresponding LiteralType. """ original_type = typ typ = get_proper_type(typ) if isinstance(typ, UnionType): new_items = [coerce_to_literal(item) for item in typ.items] return UnionType.make_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 original_type
def _effect_catch_call_hook(context: MethodContext) -> Type: f_type = _get_callable_type(context.arg_types[0][0], context) if len(context.type.args) == 1: return context.default_return_type.copy_modified( arg_types=f_type.arg_types, arg_kinds=f_type.arg_kinds, arg_names=f_type.arg_names) args = context.type.args error_union = UnionType.make_union(args) effect_type = get_proper_type(context.default_return_type.ret_type) r, e, a = effect_type.args effect_type = effect_type.copy_modified(args=[r, error_union, a]) return context.default_return_type.copy_modified( ret_type=effect_type, arg_types=f_type.arg_types, arg_kinds=f_type.arg_kinds, arg_names=f_type.arg_names)
def _change_decorator_function_type( decorated: CallableType, decorator: CallableType, provided_arguments: List[str], ) -> CallableType: decorator.arg_kinds = decorated.arg_kinds decorator.arg_names = decorated.arg_names # Mark provided arguments as optional decorator.arg_types = copy.copy(decorated.arg_types) for argument in provided_arguments: index = decorated.arg_names.index(argument) decorated_type = decorated.arg_types[index] decorator.arg_types[index] = UnionType.make_union( [decorated_type, NoneType()]) decorated.arg_kinds[index] = ARG_NAMED_OPT return decorator
def convert_any_to_type(typ: MypyType, referred_to_type: MypyType) -> MypyType: if isinstance(typ, UnionType): converted_items = [] for item in typ.items: converted_items.append(convert_any_to_type(item, referred_to_type)) return UnionType.make_union(converted_items, line=typ.line, column=typ.column) if isinstance(typ, Instance): args = [] for default_arg in typ.args: if isinstance(default_arg, AnyType): args.append(referred_to_type) else: args.append(default_arg) return reparametrize_instance(typ, args) if isinstance(typ, AnyType): return referred_to_type return typ
def transform_parameter_to_type( param: oas.OASParameter, handler_arg_type: ProperType, handler_arg_default_value: HandlerArgDefaultValue, ctx: FunctionContext, ) -> Type: oas_is_required = param.required handler_has_default = (handler_arg_default_value is not _ARG_NO_DEFAULT_VALUE_MARKER) if not oas_is_required and not handler_has_default: needs_optional = True elif isinstance(handler_arg_default_value, type(None)): needs_optional = True else: needs_optional = False if isinstance(param.schema, tuple): oas_type, _ = param.schema items = [transform_oas_type( oas_type, handler_arg_type, ctx, )] else: items = [ transform_oas_type( v.schema, handler_arg_type, ctx, ) for v in param.schema.values() ] if needs_optional: items.append(NoneType()) return simplify_union( UnionType.make_union( items=items, line=(handler_arg_type.line or handler_arg_type.end_line) or -1, column=handler_arg_type.column, ), )
def build_dict_type(self, expr: FormatStringExpr) -> Type: """Build expected mapping type for right operand in % formatting.""" any_type = AnyType(TypeOfAny.special_form) if self.chk.options.python_version >= (3, 0): if isinstance(expr, BytesExpr): bytes_type = self.chk.named_generic_type('builtins.bytes', []) return self.chk.named_generic_type('typing.Mapping', [bytes_type, any_type]) elif isinstance(expr, StrExpr): str_type = self.chk.named_generic_type('builtins.str', []) return self.chk.named_generic_type('typing.Mapping', [str_type, any_type]) else: assert False, "There should not be UnicodeExpr on Python 3" else: str_type = self.chk.named_generic_type('builtins.str', []) unicode_type = self.chk.named_generic_type('builtins.unicode', []) str_map = self.chk.named_generic_type('typing.Mapping', [str_type, any_type]) unicode_map = self.chk.named_generic_type('typing.Mapping', [unicode_type, any_type]) return UnionType.make_union([str_map, unicode_map])
def _effect_lift_call_hook(context: MethodContext) -> Type: try: f = context.type.args[0] if isinstance(f, AnyType): return context.default_return_type as_ = [] rs = [] es = [] for effect_type in context.arg_types: r, e, a = get_proper_type(effect_type[0]).args as_.append(a) rs.append(r) es.append(e) inferred = infer.infer_function_type_arguments( f, as_, [kind for kinds in context.arg_kinds for kind in kinds], [[i, i] for i in range(len(as_))]) a = context.api.expr_checker.apply_inferred_arguments( f, inferred, context.context).ret_type r = reduce(_combine_environments, rs) e = UnionType.make_union(sorted(set(es), key=str)) return context.default_return_type.copy_modified(args=[r, e, a]) except AttributeError: return context.default_return_type
def restrict_subtype_away(t: Type, s: Type) -> Type: """Return t minus s. If we can't determine a precise result, return a supertype of the ideal result (just t is a valid result). This is used for type inference of runtime type checks such as isinstance. Currently this just removes elements of a union type. """ if isinstance(t, UnionType): # Since runtime type checks will ignore type arguments, erase the types. erased_s = erase_type(s) # TODO: Implement more robust support for runtime isinstance() checks, # see issue #3827 new_items = [item for item in t.relevant_items() if (not (is_proper_subtype(erase_type(item), erased_s) or is_proper_subtype(item, erased_s)) or isinstance(item, AnyType))] return UnionType.make_union(new_items) else: return t
def make_simplified_union(items: Sequence[Type], line: int = -1, column: int = -1, *, keep_erased: bool = False, contract_literals: bool = True) -> ProperType: """Build union type with redundant union items removed. If only a single item remains, this may return a non-union type. Examples: * [int, str] -> Union[int, str] * [int, object] -> object * [int, int] -> int * [int, Any] -> Union[int, Any] (Any types are not simplified away!) * [Any, Any] -> Any Note: This must NOT be used during semantic analysis, since TypeInfos may not be fully initialized. The keep_erased flag is used for type inference against union types containing type variables. If set to True, keep all ErasedType items. """ items = get_proper_types(items) while any(isinstance(typ, UnionType) for typ in items): all_items: List[ProperType] = [] for typ in items: if isinstance(typ, UnionType): all_items.extend(get_proper_types(typ.items)) else: all_items.append(typ) items = all_items from mypy.subtypes import is_proper_subtype removed: Set[int] = set() seen: Set[Tuple[str, str]] = set() # NB: having a separate fast path for Union of Literal and slow path for other things # would arguably be cleaner, however it breaks down when simplifying the Union of two # different enum types as try_expanding_enum_to_union works recursively and will # trigger intermediate simplifications that would render the fast path useless for i, item in enumerate(items): if i in removed: continue # Avoid slow nested for loop for Union of Literal of strings/enums (issue #9169) if is_simple_literal(item): assert isinstance(item, LiteralType) assert isinstance(item.value, str) k = (item.value, item.fallback.type.fullname) if k in seen: removed.add(i) continue # NB: one would naively expect that it would be safe to skip the slow path # always for literals. One would be sorely mistaken. Indeed, some simplifications # such as that of None/Optional when strict optional is false, do require that we # proceed with the slow path. Thankfully, all literals will have the same subtype # relationship to non-literal types, so we only need to do that walk for the first # literal, which keeps the fast path fast even in the presence of a mixture of # literals and other types. safe_skip = len(seen) > 0 seen.add(k) if safe_skip: continue # Keep track of the truishness info for deleted subtypes which can be relevant cbt = cbf = False for j, tj in enumerate(items): # NB: we don't need to check literals as the fast path above takes care of that if (i != j and not is_simple_literal(tj) and is_proper_subtype( tj, item, keep_erased_types=keep_erased) and is_redundant_literal_instance(item, tj) # XXX? ): # We found a redundant item in the union. removed.add(j) cbt = cbt or tj.can_be_true cbf = cbf or tj.can_be_false # if deleted subtypes had more general truthiness, use that if not item.can_be_true and cbt: items[i] = true_or_false(item) elif not item.can_be_false and cbf: items[i] = true_or_false(item) simplified_set = [items[i] for i in range(len(items)) if i not in removed] # If more than one literal exists in the union, try to simplify if (contract_literals and sum(isinstance(item, LiteralType) for item in simplified_set) > 1): simplified_set = try_contracting_literals_in_union(simplified_set) return UnionType.make_union(simplified_set, line, column)
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 UnionType.make_simplified_union([self.visit_unbound_type(t), NoneTyp()]) sym = self.lookup(t.name, t) 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('Internal error (node is None, kind={})'.format(sym.kind), t) return AnyType() fullname = sym.node.fullname() if sym.kind == BOUND_TVAR: if len(t.args) > 0: self.fail('Type variable "{}" used with arguments'.format( t.name), t) assert sym.tvar_def is not None return TypeVarType(sym.tvar_def, t.line) elif fullname == 'builtins.None': if experiments.STRICT_OPTIONAL: return NoneTyp(is_ret_type=t.is_ret_type) else: return Void() elif fullname == 'typing.Any': return AnyType() elif fullname == 'typing.Tuple': if len(t.args) == 0 and not t.empty_tuple_index: # Bare 'Tuple' is same as 'tuple' return self.builtin_type('builtins.tuple') if len(t.args) == 2 and isinstance(t.args[1], EllipsisType): # Tuple[T, ...] (uniform, variable-length tuple) instance = self.builtin_type('builtins.tuple', [t.args[0].accept(self)]) 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) items = [item for item in items if not isinstance(item, Void)] return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: self.fail('Optional[...] must have exactly one type argument', t) return AnyType() items = self.anal_array(t.args) if experiments.STRICT_OPTIONAL: return UnionType.make_simplified_union([items[0], NoneTyp()]) else: # Without strict Optional checking Optional[t] is just an alias for t. return items[0] elif fullname == 'typing.Callable': return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: return TypeType(AnyType(), line=t.line) if len(t.args) != 1: self.fail('Type[...] must have exactly one type argument', t) items = self.anal_array(t.args) item = items[0] return TypeType(item, line=t.line) elif sym.kind == TYPE_ALIAS: override = sym.type_override an_args = self.anal_array(t.args) all_vars = self.get_type_var_names(override) exp_len = len(all_vars) 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, ...] return self.replace_alias_tvars(override, all_vars, [AnyType()] * exp_len, t.line, t.column) if exp_len == 0 and act_len == 0: return override if act_len != exp_len: self.fail('Bad number of arguments for type alias, expected: %s, given: %s' % (exp_len, act_len), t) return t return self.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() # Allow unbound type variables when defining an alias if not (self.aliasing and sym.kind == UNBOUND_TVAR): self.fail('Invalid type "{}"'.format(name), t) return t info = sym.node # type: TypeInfo if len(t.args) > 0 and info.fullname() == 'builtins.tuple': return TupleType(self.anal_array(t.args), Instance(info, [AnyType()], t.line), 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) 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('Generic tuple types not supported', t) return AnyType() 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('Generic TypedDict types not supported', t) return AnyType() # Create a named TypedDictType return td.copy_modified(item_types=self.anal_array(list(td.items.values())), fallback=instance) return instance else: return AnyType()
def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: """Return this attribute as an argument to __init__.""" assert self.init init_type = self.info[self.name].type if self.converter.name: # When a converter is set the init_type is overridden by the first argument # of the converter method. converter = lookup_qualified_stnode(ctx.api.modules, self.converter.name, True) if not converter: # The converter may be a local variable. Check there too. converter = ctx.api.lookup_qualified(self.converter.name, self.info, True) # Get the type of the converter. converter_type = None if converter and isinstance(converter.node, TypeInfo): from mypy.checkmember import type_object_type # To avoid import cycle. converter_type = type_object_type(converter.node, ctx.api.builtin_type) elif converter and isinstance(converter.node, OverloadedFuncDef): converter_type = converter.node.type elif converter and converter.type: converter_type = converter.type init_type = None if isinstance(converter_type, CallableType) and converter_type.arg_types: init_type = ctx.api.anal_type(converter_type.arg_types[0]) elif isinstance(converter_type, Overloaded): types = [] # type: List[Type] for item in converter_type.items(): # Walk the overloads looking for methods that can accept one argument. num_arg_types = len(item.arg_types) if not num_arg_types: continue if num_arg_types > 1 and any(kind == ARG_POS for kind in item.arg_kinds[1:]): continue types.append(item.arg_types[0]) # Make a union of all the valid types. if types: args = UnionType.make_simplified_union(types) init_type = ctx.api.anal_type(args) if self.converter.is_attr_converters_optional and init_type: # If the converter was attr.converter.optional(type) then add None to # the allowed init_type. init_type = UnionType.make_union([init_type, NoneTyp()]) if not init_type: ctx.api.fail("Cannot determine __init__ type from converter", self.context) init_type = AnyType(TypeOfAny.from_error) elif self.converter.name == '': # This means we had a converter but it's not of a type we can infer. # Error was shown in _get_converter_name init_type = AnyType(TypeOfAny.from_error) if init_type is None: if ctx.api.options.disallow_untyped_defs: # This is a compromise. If you don't have a type here then the # __init__ will be untyped. But since the __init__ is added it's # pointing at the decorator. So instead we also show the error in the # assignment, which is where you would fix the issue. node = self.info[self.name].node assert node is not None ctx.api.msg.need_annotation_for_var(node, self.context) # Convert type not set to Any. init_type = AnyType(TypeOfAny.unannotated) if self.kw_only: arg_kind = ARG_NAMED_OPT if self.has_default else ARG_NAMED else: arg_kind = ARG_OPT if self.has_default else ARG_POS # Attrs removes leading underscores when creating the __init__ arguments. return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind)
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('Internal error (node is None, kind={})'.format(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(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('Can\'t use bound type variable "{}"' ' to define generic alias'.format(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('Type variable "{}" used with arguments'.format( 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(messages.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('Optional[...] must have exactly one type 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('Type[...] must have exactly one type argument', 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('Invalid type: ClassVar nested inside other 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('ClassVar[...] must have at most one type argument', t) return AnyType(TypeOfAny.from_error) item = self.anal_type(t.args[0]) if isinstance(item, TypeVarType) or get_type_vars(item): self.fail('Invalid type: 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('Bad number of arguments for type alias, expected: %s, given: %s' % (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('Unsupported forward reference to "{}"'.format(t.name), t) return AnyType(TypeOfAny.from_error) return ForwardRef(t) self.fail('Invalid type "{}"'.format(name), t) if self.third_pass and sym.kind == TVAR: self.note_func("Forward references to type variables are 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('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('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('Invalid type "{}"'.format(t.name), t) return AnyType(TypeOfAny.from_error) return AnyType(TypeOfAny.special_form)
def is_overlapping_types(left: Type, right: Type, ignore_promotions: bool = False, prohibit_none_typevar_overlap: bool = False) -> bool: """Can a value of type 'left' also be of type 'right' or vice-versa? If 'ignore_promotions' is True, we ignore promotions while checking for overlaps. If 'prohibit_none_typevar_overlap' is True, we disallow None from overlapping with TypeVars (in both strict-optional and non-strict-optional mode). """ def _is_overlapping_types(left: Type, right: Type) -> bool: '''Encode the kind of overlapping check to perform. This function mostly exists so we don't have to repeat keyword arguments everywhere.''' return is_overlapping_types( left, right, ignore_promotions=ignore_promotions, prohibit_none_typevar_overlap=prohibit_none_typevar_overlap) # We should never encounter this type. if isinstance(left, PartialType) or isinstance(right, PartialType): assert False, "Unexpectedly encountered partial type" # We should also never encounter these types, but it's possible a few # have snuck through due to unrelated bugs. For now, we handle these # in the same way we handle 'Any'. # # TODO: Replace these with an 'assert False' once we are more confident. illegal_types = (UnboundType, ErasedType, DeletedType) if isinstance(left, illegal_types) or isinstance(right, illegal_types): return True # 'Any' may or may not be overlapping with the other type if isinstance(left, AnyType) or isinstance(right, AnyType): return True # When running under non-strict optional mode, simplify away types of # the form 'Union[A, B, C, None]' into just 'Union[A, B, C]'. if not state.strict_optional: if isinstance(left, UnionType): left = UnionType.make_union(left.relevant_items()) if isinstance(right, UnionType): right = UnionType.make_union(right.relevant_items()) # We check for complete overlaps next as a general-purpose failsafe. # If this check fails, we start checking to see if there exists a # *partial* overlap between types. # # These checks will also handle the NoneTyp and UninhabitedType cases for us. if (is_proper_subtype(left, right, ignore_promotions=ignore_promotions) or is_proper_subtype(right, left, ignore_promotions=ignore_promotions)): return True # See the docstring for 'get_possible_variants' for more info on what the # following lines are doing. left_possible = get_possible_variants(left) right_possible = get_possible_variants(right) # We start by checking multi-variant types like Unions first. We also perform # the same logic if either type happens to be a TypeVar. # # Handling the TypeVars now lets us simulate having them bind to the corresponding # type -- if we deferred these checks, the "return-early" logic of the other # checks will prevent us from detecting certain overlaps. # # If both types are singleton variants (and are not TypeVars), we've hit the base case: # we skip these checks to avoid infinitely recursing. def is_none_typevar_overlap(t1: Type, t2: Type) -> bool: return isinstance(t1, NoneTyp) and isinstance(t2, TypeVarType) if prohibit_none_typevar_overlap: if is_none_typevar_overlap(left, right) or is_none_typevar_overlap(right, left): return False if (len(left_possible) > 1 or len(right_possible) > 1 or isinstance(left, TypeVarType) or isinstance(right, TypeVarType)): for l in left_possible: for r in right_possible: if _is_overlapping_types(l, r): return True return False # Now that we've finished handling TypeVars, we're free to end early # if one one of the types is None and we're running in strict-optional mode. # (None only overlaps with None in strict-optional mode). # # We must perform this check after the TypeVar checks because # a TypeVar could be bound to None, for example. if state.strict_optional and isinstance(left, NoneTyp) != isinstance(right, NoneTyp): return False # Next, we handle single-variant types that may be inherently partially overlapping: # # - TypedDicts # - Tuples # # If we cannot identify a partial overlap and end early, we degrade these two types # into their 'Instance' fallbacks. if isinstance(left, TypedDictType) and isinstance(right, TypedDictType): return are_typed_dicts_overlapping(left, right, ignore_promotions=ignore_promotions) elif isinstance(left, TypedDictType): left = left.fallback elif isinstance(right, TypedDictType): right = right.fallback if is_tuple(left) and is_tuple(right): return are_tuples_overlapping(left, right, ignore_promotions=ignore_promotions) elif isinstance(left, TupleType): left = left.fallback elif isinstance(right, TupleType): right = right.fallback # Next, we handle single-variant types that cannot be inherently partially overlapping, # but do require custom logic to inspect. # # As before, we degrade into 'Instance' whenever possible. if isinstance(left, TypeType) and isinstance(right, TypeType): # TODO: Can Callable[[...], T] and Type[T] be partially overlapping? return _is_overlapping_types(left.item, right.item) if isinstance(left, CallableType) and isinstance(right, CallableType): return is_callable_compatible(left, right, is_compat=_is_overlapping_types, ignore_pos_arg_names=True, allow_partial_overlap=True) elif isinstance(left, CallableType): left = left.fallback elif isinstance(right, CallableType): right = right.fallback if isinstance(left, LiteralType) and isinstance(right, LiteralType): return left == right elif isinstance(left, LiteralType): left = left.fallback elif isinstance(right, LiteralType): right = right.fallback # Finally, we handle the case where left and right are instances. if isinstance(left, Instance) and isinstance(right, Instance): if left.type.is_protocol and is_protocol_implementation(right, left): return True if right.type.is_protocol and is_protocol_implementation(left, right): return True # Two unrelated types cannot be partially overlapping: they're disjoint. # We don't need to handle promotions because they've already been handled # by the calls to `is_subtype(...)` up above (and promotable types never # have any generic arguments we need to recurse on). if left.type.has_base(right.type.fullname()): left = map_instance_to_supertype(left, right.type) elif right.type.has_base(left.type.fullname()): right = map_instance_to_supertype(right, left.type) else: return False if len(left.args) == len(right.args): # Note: we don't really care about variance here, since the overlapping check # is symmetric and since we want to return 'True' even for partial overlaps. # # For example, suppose we have two types Wrapper[Parent] and Wrapper[Child]. # It doesn't matter whether Wrapper is covariant or contravariant since # either way, one of the two types will overlap with the other. # # Similarly, if Wrapper was invariant, the two types could still be partially # overlapping -- what if Wrapper[Parent] happened to contain only instances of # specifically Child? # # Or, to use a more concrete example, List[Union[A, B]] and List[Union[B, C]] # would be considered partially overlapping since it's possible for both lists # to contain only instances of B at runtime. for left_arg, right_arg in zip(left.args, right.args): if _is_overlapping_types(left_arg, right_arg): return True return False # We ought to have handled every case by now: we conclude the # two types are not overlapping, either completely or partially. # # Note: it's unclear however, whether returning False is the right thing # to do when inferring reachability -- see https://github.com/python/mypy/issues/5529 assert type(left) != type(right) return False
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 UnionType.make_simplified_union([self.visit_unbound_type(t), NoneTyp()]) sym = self.lookup(t.name, t) 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('Internal error (node is None, kind={})'.format(sym.kind), t) return AnyType() fullname = sym.node.fullname() if sym.kind == BOUND_TVAR: if len(t.args) > 0: self.fail('Type variable "{}" used with arguments'.format( t.name), t) assert sym.tvar_def is not None return TypeVarType(sym.tvar_def, t.line) elif fullname == 'builtins.None': if experiments.STRICT_OPTIONAL: return NoneTyp(is_ret_type=t.is_ret_type) else: return Void() elif fullname == 'typing.Any': return AnyType() elif fullname == 'typing.Tuple': if len(t.args) == 2 and isinstance(t.args[1], EllipsisType): # Tuple[T, ...] (uniform, variable-length tuple) node = self.lookup_fqn_func('builtins.tuple') tuple_info = cast(TypeInfo, node.node) return Instance(tuple_info, [t.args[0].accept(self)], t.line) return self.tuple_type(self.anal_array(t.args)) elif fullname == 'typing.Union': items = self.anal_array(t.args) items = [item for item in items if not isinstance(item, Void)] return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: self.fail('Optional[...] must have exactly one type argument', t) return AnyType() items = self.anal_array(t.args) if experiments.STRICT_OPTIONAL: return UnionType.make_simplified_union([items[0], NoneTyp()]) else: # Without strict Optional checking Optional[t] is just an alias for t. return items[0] elif fullname == 'typing.Callable': return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: return TypeType(AnyType(), line=t.line) if len(t.args) != 1: self.fail('Type[...] must have exactly one type argument', t) items = self.anal_array(t.args) item = items[0] return TypeType(item, line=t.line) elif sym.kind == TYPE_ALIAS: # TODO: Generic type aliases. return sym.type_override 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() self.fail('Invalid type "{}"'.format(name), t) return t info = sym.node # type: TypeInfo if len(t.args) > 0 and info.fullname() == 'builtins.tuple': return TupleType(self.anal_array(t.args), Instance(info, [AnyType()], t.line), 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) tup = info.tuple_type if tup is None: return instance else: # The class has a Tuple[...] base class so it will be # represented as a tuple type. if t.args: self.fail('Generic tuple types not supported', t) return AnyType() return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) else: return AnyType()
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 visit_unbound_type(self, t: UnboundType) -> Type: sym = self.lookup(t.name, t) if sym is not None: fullname = sym.node.fullname() if sym.kind == BOUND_TVAR: if len(t.args) > 0: self.fail('Type variable "{}" used with arguments'.format( t.name), t) tvar_expr = cast(TypeVarExpr, sym.node) return TypeVarType(t.name, sym.tvar_id, tvar_expr.values, self.builtin_type('builtins.object'), tvar_expr.variance, t.line) elif fullname == 'builtins.None': return Void() elif fullname == 'typing.Any': return AnyType() elif fullname == 'typing.Tuple': if len(t.args) == 2 and isinstance(t.args[1], EllipsisType): # Tuple[T, ...] (uniform, variable-length tuple) node = self.lookup_fqn_func('builtins.tuple') info = cast(TypeInfo, node.node) return Instance(info, [t.args[0].accept(self)], t.line) return TupleType(self.anal_array(t.args), self.builtin_type('builtins.tuple')) elif fullname == 'typing.Union': items = self.anal_array(t.args) items = [item for item in items if not isinstance(item, Void)] return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: self.fail('Optional[...] must have exactly one type argument', t) items = self.anal_array(t.args) # Currently Optional[t] is just an alias for t. return items[0] elif fullname == 'typing.Callable': return self.analyze_callable_type(t) elif sym.kind == TYPE_ALIAS: # TODO: Generic type aliases. return sym.type_override 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() 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), Instance(info, [AnyType()], t.line), 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) if info.tuple_type is None: return instance else: # The class has a Tuple[...] base class so it will be # represented as a tuple type. if t.args: self.fail('Generic tuple types not supported', t) return AnyType() return TupleType(self.anal_array(info.tuple_type.items), fallback=instance, line=t.line) else: return t
def make_optional(typ: MypyType) -> MypyType: return UnionType.make_union([typ, NoneTyp()])
def visit_unbound_type(self, t: UnboundType) -> Type: sym = self.lookup(t.name, t) if sym is not None: fullname = sym.node.fullname() if sym.kind == TVAR: if len(t.args) > 0: self.fail('Type variable "{}" used with arguments'.format( t.name), t) values = cast(TypeVarExpr, sym.node).values return TypeVarType(t.name, sym.tvar_id, values, self.builtin_type('builtins.object'), t.line) elif fullname == 'builtins.None': return Void() elif fullname == 'typing.Any': return AnyType() elif fullname == 'typing.Tuple': return TupleType(self.anal_array(t.args), self.builtin_type('builtins.tuple')) elif fullname == 'typing.Union': items = self.anal_array(t.args) items = [item for item in items if not isinstance(item, Void)] return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: self.fail('Optional[...] must have exactly one type argument', t) items = self.anal_array(t.args) # Currently Optional[t] is just an alias for t. return items[0] elif fullname == 'typing.Callable': return self.analyze_function_type(t) elif sym.kind == TYPE_ALIAS: # TODO: Generic type aliases. return sym.type_override elif not isinstance(sym.node, TypeInfo): name = sym.fullname if name is None: name = sym.node.name() 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), Instance(info, [], t.line), 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) if info.tuple_type is None: return instance else: # The class has a Tuple[...] base class so it will be # represented as a tuple type. return TupleType(self.anal_array(info.tuple_type.items), fallback=instance, line=t.line) else: return t