MCPcopy
hub / github.com/python/mypy / check_compatibility

Method check_compatibility

mypy/checker.py:3110–3196  ·  view source on GitHub ↗

Check if attribute name in base1 is compatible with base2 in multiple inheritance. Assume base1 comes before base2 in the MRO, and that base1 and base2 don't have a direct subclass relationship (i.e., the compatibility requirement only derives from multiple inheritance).

(
        self, name: str, base1: TypeInfo, base2: TypeInfo, ctx: TypeInfo
    )

Source from the content-addressed store, hash-verified

3108 self.check_compatibility(name, base, base2, typ)
3109
3110 def check_compatibility(
3111 self, name: str, base1: TypeInfo, base2: TypeInfo, ctx: TypeInfo
3112 ) -> None:
3113 """Check if attribute name in base1 is compatible with base2 in multiple inheritance.
3114
3115 Assume base1 comes before base2 in the MRO, and that base1 and base2 don't have
3116 a direct subclass relationship (i.e., the compatibility requirement only derives from
3117 multiple inheritance).
3118
3119 This check verifies that a definition taken from base1 (and mapped to the current
3120 class ctx), is type compatible with the definition taken from base2 (also mapped), so
3121 that unsafe subclassing like this can be detected:
3122 class A(Generic[T]):
3123 def foo(self, x: T) -> None: ...
3124
3125 class B:
3126 def foo(self, x: str) -> None: ...
3127
3128 class C(B, A[int]): ... # this is unsafe because...
3129
3130 x: A[int] = C()
3131 x.foo # ...runtime type is (str) -> None, while static type is (int) -> None
3132 """
3133 if name in ("__init__", "__new__", "__init_subclass__"):
3134 # __init__ and friends can be incompatible -- it's a special case.
3135 return
3136 first = base1.names[name]
3137 second = base2.names[name]
3138 # Specify current_class explicitly as this function is called after leaving the class.
3139 first_type, _ = self.node_type_from_base(name, base1, ctx, current_class=ctx)
3140 second_type, _ = self.node_type_from_base(name, base2, ctx, current_class=ctx)
3141
3142 # TODO: use more principled logic to decide is_subtype() vs is_equivalent().
3143 # We should rely on mutability of superclass node, not on types being Callable.
3144 # (in particular handle settable properties with setter type different from getter).
3145
3146 p_first_type = get_proper_type(first_type)
3147 p_second_type = get_proper_type(second_type)
3148 if isinstance(p_first_type, FunctionLike) and isinstance(p_second_type, FunctionLike):
3149 if p_first_type.is_type_obj() and p_second_type.is_type_obj():
3150 # For class objects only check the subtype relationship of the classes,
3151 # since we allow incompatible overrides of '__init__'/'__new__'
3152 ok = is_subtype(
3153 left=fill_typevars_with_any(p_first_type.type_object()),
3154 right=fill_typevars_with_any(p_second_type.type_object()),
3155 )
3156 else:
3157 assert first_type and second_type
3158 ok = is_subtype(first_type, second_type, ignore_pos_arg_names=True)
3159 elif first_type and second_type:
3160 if second.node is not None and not self.is_writable_attribute(second.node):
3161 ok = is_subtype(first_type, second_type)
3162 else:
3163 ok = is_equivalent(first_type, second_type)
3164 if ok:
3165 if (
3166 first.node
3167 and second.node

Callers 1

Calls 15

node_type_from_baseMethod · 0.95
is_writable_attributeMethod · 0.95
get_proper_typeFunction · 0.90
is_subtypeFunction · 0.90
fill_typevars_with_anyFunction · 0.90
is_equivalentFunction · 0.90
is_final_nodeFunction · 0.90
isinstanceFunction · 0.85
is_propertyFunction · 0.85
is_privateFunction · 0.85

Tested by

no test coverage detected