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
)
| 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 |
no test coverage detected