def is_special_target(right: ProperType) -> bool: """Whitelist some special cases for use in isinstance() with improper types.""" if isinstance(right, CallableType) and right.is_type_obj(): if right.type_object().fullname() == 'builtins.tuple': # Used with Union[Type, Tuple[Type, ...]]. return True if right.type_object().fullname() in ('mypy.types.Type', 'mypy.types.ProperType', 'mypy.types.TypeAliasType'): # Special case: things like assert isinstance(typ, ProperType) are always OK. return True if right.type_object().fullname() in ('mypy.types.UnboundType', 'mypy.types.TypeVarType', 'mypy.types.RawExpressionType', 'mypy.types.EllipsisType', 'mypy.types.StarType', 'mypy.types.TypeList', 'mypy.types.CallableArgument', 'mypy.types.PartialType', 'mypy.types.ErasedType'): # Special case: these are not valid targets for a type alias and thus safe. # TODO: introduce a SyntheticType base to simplify this? return True elif isinstance(right, TupleType): return all(is_special_target(t) for t in get_proper_types(right.items)) return False
def is_dangerous_target(typ: ProperType) -> bool: """Is this a dangerous target (right argument) for an isinstance() check?""" if isinstance(typ, TupleType): return any(is_dangerous_target(get_proper_type(t)) for t in typ.items) if isinstance(typ, CallableType) and typ.is_type_obj(): return typ.type_object().has_base('mypy.types.Type') return False
def add_class_tvars(t: ProperType, itype: Instance, isuper: Optional[Instance], is_classmethod: bool, builtin_type: Callable[[str], Instance], original_type: Type) -> Type: """Instantiate type variables during analyze_class_attribute_access, e.g T and Q in the following: class A(Generic[T]): @classmethod def foo(cls: Type[Q]) -> Tuple[T, Q]: ... class B(A[str]): pass B.foo() original_type is the value of the type B in the expression B.foo() """ # TODO: verify consistency between Q and T info = itype.type # type: TypeInfo if is_classmethod: assert isuper is not None t = expand_type_by_instance(t, isuper) # We add class type variables if the class method is accessed on class object # without applied type arguments, this matches the behavior of __init__(). # For example (continuing the example in docstring): # A # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any] # A[int] # The type of callable is def () -> A[int] # and # A.foo # The type is generic def [T] () -> Tuple[T, A[T]] # A[int].foo # The type is non-generic def () -> Tuple[int, A[int]] # # This behaviour is useful for defining alternative constructors for generic classes. # To achieve such behaviour, we add the class type variables that are still free # (i.e. appear in the return type of the class object on which the method was accessed). free_ids = {t.id for t in itype.args if isinstance(t, TypeVarType)} if isinstance(t, CallableType): # NOTE: in practice either all or none of the variables are free, since # visit_type_application() will detect any type argument count mismatch and apply # a correct number of Anys. tvars = [ TypeVarDef(n, n, i + 1, [], builtin_type('builtins.object'), tv.variance) for (i, n), tv in zip(enumerate(info.type_vars), info.defn.type_vars) # use 'is' to avoid id clashes with unrelated variables if any(tv.id is id for id in free_ids) ] if is_classmethod: t = bind_self(t, original_type, is_classmethod=True) return t.copy_modified(variables=tvars + t.variables) elif isinstance(t, Overloaded): return Overloaded([ cast( CallableType, add_class_tvars(item, itype, isuper, is_classmethod, builtin_type, original_type)) for item in t.items() ]) return t
def add_class_tvars(t: ProperType, itype: Instance, isuper: Optional[Instance], is_classmethod: bool, builtin_type: Callable[[str], Instance], original_type: Type, original_vars: Optional[List[TypeVarDef]] = None) -> Type: """Instantiate type variables during analyze_class_attribute_access, e.g T and Q in the following: class A(Generic[T]): @classmethod def foo(cls: Type[Q]) -> Tuple[T, Q]: ... class B(A[str]): pass B.foo() original_type is the value of the type B in the expression B.foo() or the corresponding component in case if a union (this is used to bind the self-types); original_vars are type variables of the class callable on which the method was accessed. """ # TODO: verify consistency between Q and T if is_classmethod: assert isuper is not None t = get_proper_type(expand_type_by_instance(t, isuper)) # We add class type variables if the class method is accessed on class object # without applied type arguments, this matches the behavior of __init__(). # For example (continuing the example in docstring): # A # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any] # A[int] # The type of callable is def () -> A[int] # and # A.foo # The type is generic def [T] () -> Tuple[T, A[T]] # A[int].foo # The type is non-generic def () -> Tuple[int, A[int]] # # This behaviour is useful for defining alternative constructors for generic classes. # To achieve such behaviour, we add the class type variables that are still free # (i.e. appear in the return type of the class object on which the method was accessed). if isinstance(t, CallableType): tvars = original_vars if original_vars is not None else [] if is_classmethod: t = bind_self(t, original_type, is_classmethod=True) return t.copy_modified(variables=tvars + t.variables) elif isinstance(t, Overloaded): return Overloaded([ cast( CallableType, add_class_tvars(item, itype, isuper, is_classmethod, builtin_type, original_type, original_vars=original_vars)) for item in t.items() ]) return t
def add_class_tvars(t: ProperType, isuper: Optional[Instance], is_classmethod: bool, original_type: Type, original_vars: Optional[Sequence[TypeVarLikeDef]] = None) -> Type: """Instantiate type variables during analyze_class_attribute_access, e.g T and Q in the following: class A(Generic[T]): @classmethod def foo(cls: Type[Q]) -> Tuple[T, Q]: ... class B(A[str]): pass B.foo() Args: t: Declared type of the method (or property) isuper: Current instance mapped to the superclass where method was defined, this is usually done by map_instance_to_supertype() is_classmethod: True if this method is decorated with @classmethod original_type: The value of the type B in the expression B.foo() or the corresponding component in case of a union (this is used to bind the self-types) original_vars: Type variables of the class callable on which the method was accessed Returns: Expanded method type with added type variables (when needed). """ # TODO: verify consistency between Q and T # We add class type variables if the class method is accessed on class object # without applied type arguments, this matches the behavior of __init__(). # For example (continuing the example in docstring): # A # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any] # A[int] # The type of callable is def () -> A[int] # and # A.foo # The type is generic def [T] () -> Tuple[T, A[T]] # A[int].foo # The type is non-generic def () -> Tuple[int, A[int]] # # This behaviour is useful for defining alternative constructors for generic classes. # To achieve such behaviour, we add the class type variables that are still free # (i.e. appear in the return type of the class object on which the method was accessed). if isinstance(t, CallableType): tvars = original_vars if original_vars is not None else [] if is_classmethod: t = freshen_function_type_vars(t) t = bind_self(t, original_type, is_classmethod=True) assert isuper is not None t = cast(CallableType, expand_type_by_instance(t, isuper)) freeze_type_vars(t) return t.copy_modified(variables=list(tvars) + list(t.variables)) elif isinstance(t, Overloaded): return Overloaded([cast(CallableType, add_class_tvars(item, isuper, is_classmethod, original_type, original_vars=original_vars)) for item in t.items()]) if isuper is not None: t = cast(ProperType, expand_type_by_instance(t, isuper)) return t
def get_typed_dict_type( ctx: FunctionContext, handler_arg_type: ProperType, oas_type: oas_model.OASObjectType, ) -> TypedDictType: if isinstance(handler_arg_type, UnionType): td_type_fallback = next( try_getting_instance_fallback(get_proper_type(x)) for x in handler_arg_type.relevant_items()) else: td_type_fallback = try_getting_instance_fallback(handler_arg_type) assert td_type_fallback is not None return TypedDictType( items=OrderedDict({ oas_prop_name: transform_oas_type(oas_prop_type, handler_arg_type, ctx) for oas_prop_name, oas_prop_type in oas_type.properties.items() }), required_keys=oas_type.required, fallback=td_type_fallback, line=td_type_fallback.line, column=td_type_fallback.column, )
def freeze_type_vars(member_type: ProperType) -> None: if isinstance(member_type, CallableType): for v in member_type.variables: v.id.meta_level = 0 if isinstance(member_type, Overloaded): for it in member_type.items(): for v in it.variables: v.id.meta_level = 0
def copy_type(t: ProperType) -> ProperType: """Create a shallow copy of a type. This can be used to mutate the copy with truthiness information. Classes compiled with mypyc don't support copy.copy(), so we need a custom implementation. """ return t.accept(TypeShallowCopier())
def _type_object_overlap(left: ProperType, right: ProperType) -> bool: """Special cases for type object types overlaps.""" # TODO: these checks are a bit in gray area, adjust if they cause problems. # 1. Type[C] vs Callable[..., C], where the latter is class object. if isinstance(left, TypeType) and isinstance(right, CallableType) and right.is_type_obj(): return _is_overlapping_types(left.item, right.ret_type) # 2. Type[C] vs Meta, where Meta is a metaclass for C. if (isinstance(left, TypeType) and isinstance(left.item, Instance) and isinstance(right, Instance)): left_meta = left.item.type.metaclass_type if left_meta is not None: return _is_overlapping_types(left_meta, right) # builtins.type (default metaclass) overlaps with all metaclasses return right.type.has_base('builtins.type') # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks. return False
def add_operator_method_dependency_for_type(self, typ: ProperType, method: str) -> None: # Note that operator methods can't be (non-metaclass) methods of type objects # (that is, TypeType objects or Callables representing a type). if isinstance(typ, TypeVarType): typ = get_proper_type(typ.upper_bound) if isinstance(typ, TupleType): typ = typ.partial_fallback if isinstance(typ, Instance): trigger = make_trigger(typ.type.fullname + '.' + method) self.add_dependency(trigger) elif isinstance(typ, UnionType): for item in typ.items: self.add_operator_method_dependency_for_type(get_proper_type(item), method) elif isinstance(typ, FunctionLike) and typ.is_type_obj(): self.add_operator_method_dependency_for_type(typ.fallback, method) elif isinstance(typ, TypeType): if isinstance(typ.item, Instance) and typ.item.type.metaclass_type is not None: self.add_operator_method_dependency_for_type(typ.item.type.metaclass_type, method)
def adjust_tuple(left: ProperType, r: ProperType) -> Optional[TupleType]: """Find out if `left` is a Tuple[A, ...], and adjust its length to `right`""" if isinstance(left, Instance) and left.type.fullname() == 'builtins.tuple': n = r.length() if isinstance(r, TupleType) else 1 return TupleType([left.args[0]] * n, left) return None
def copy_common(self, t: ProperType, t2: ProperType) -> ProperType: t2.line = t.line t2.column = t.column t2.can_be_false = t.can_be_false t2.can_be_true = t.can_be_true return t2
def is_enum_overlapping_union(x: ProperType, y: ProperType) -> bool: """Return True if x is an Enum, and y is an Union with at least one Literal from x""" return (isinstance(x, Instance) and x.type.is_enum and isinstance(y, UnionType) and any( isinstance(p, LiteralType) and x.type == p.fallback.type for p in (get_proper_type(z) for z in y.relevant_items())))