Check that an instance has a valid type for a method with annotated 'self'. For example if the method is defined as: class A: def f(self: S) -> T: ... then for 'x.f' we check that type(x) <: S. If the method is overloaded, we select only overloads items that satisfy
(
functype: FunctionLike,
dispatched_arg_type: Type,
is_classmethod: bool,
context: Context,
name: str,
msg: MessageBuilder,
)
| 1054 | |
| 1055 | |
| 1056 | def check_self_arg( |
| 1057 | functype: FunctionLike, |
| 1058 | dispatched_arg_type: Type, |
| 1059 | is_classmethod: bool, |
| 1060 | context: Context, |
| 1061 | name: str, |
| 1062 | msg: MessageBuilder, |
| 1063 | ) -> FunctionLike: |
| 1064 | """Check that an instance has a valid type for a method with annotated 'self'. |
| 1065 | |
| 1066 | For example if the method is defined as: |
| 1067 | class A: |
| 1068 | def f(self: S) -> T: ... |
| 1069 | then for 'x.f' we check that type(x) <: S. If the method is overloaded, we select |
| 1070 | only overloads items that satisfy this requirement. If there are no matching |
| 1071 | overloads, an error is generated. |
| 1072 | """ |
| 1073 | items = functype.items |
| 1074 | if not items: |
| 1075 | return functype |
| 1076 | new_items = [] |
| 1077 | if is_classmethod: |
| 1078 | dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type) |
| 1079 | p_dispatched_arg_type = get_proper_type(dispatched_arg_type) |
| 1080 | |
| 1081 | for item in items: |
| 1082 | if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR): |
| 1083 | # No positional first (self) argument (*args is okay). |
| 1084 | msg.no_formal_self(name, item, context) |
| 1085 | # This is pretty bad, so just return the original signature if |
| 1086 | # there is at least one such error. |
| 1087 | return functype |
| 1088 | selfarg = get_proper_type(item.arg_types[0]) |
| 1089 | if isinstance(selfarg, Instance) and isinstance(p_dispatched_arg_type, Instance): |
| 1090 | if selfarg.type is p_dispatched_arg_type.type and selfarg.args: |
| 1091 | if not is_overlapping_types(p_dispatched_arg_type, selfarg): |
| 1092 | # This special casing is needed since `actual <: erased(template)` |
| 1093 | # logic below doesn't always work, and a more correct approach may |
| 1094 | # be tricky. |
| 1095 | continue |
| 1096 | new_items.append(item) |
| 1097 | |
| 1098 | if new_items: |
| 1099 | items = new_items |
| 1100 | new_items = [] |
| 1101 | |
| 1102 | for item in items: |
| 1103 | selfarg = get_proper_type(item.arg_types[0]) |
| 1104 | # This matches similar special-casing in bind_self(), see more details there. |
| 1105 | self_callable = name == "__call__" and isinstance(selfarg, CallableType) |
| 1106 | if self_callable or is_subtype( |
| 1107 | dispatched_arg_type, |
| 1108 | # This level of erasure matches the one in checker.check_func_def(), |
| 1109 | # better keep these two checks consistent. |
| 1110 | erase_typevars(erase_to_bound(selfarg)), |
| 1111 | # This is to work around the fact that erased ParamSpec and TypeVarTuple |
| 1112 | # callables are not always compatible with non-erased ones both ways. |
| 1113 | always_covariant=any( |
no test coverage detected
searching dependent graphs…