|
7 | 7 | Type, AnyType, CallableType, Overloaded, NoneTyp, Void, TypeVarDef,
|
8 | 8 | TupleType, TypedDictType, Instance, TypeVarId, TypeVarType, ErasedType, UnionType,
|
9 | 9 | PartialType, DeletedType, UnboundType, UninhabitedType, TypeType,
|
10 |
| - true_only, false_only, is_named_instance, function_type, |
| 10 | + true_only, false_only, is_named_instance, function_type, FunctionLike, |
11 | 11 | get_typ_args, set_typ_args,
|
12 | 12 | )
|
13 | 13 | from mypy.nodes import (
|
|
30 | 30 | from mypy import messages
|
31 | 31 | from mypy.infer import infer_type_arguments, infer_function_type_arguments
|
32 | 32 | from mypy import join
|
| 33 | +from mypy.maptype import map_instance_to_supertype |
33 | 34 | from mypy.subtypes import is_subtype, is_equivalent
|
34 | 35 | from mypy import applytype
|
35 | 36 | from mypy import erasetype
|
36 |
| -from mypy.checkmember import analyze_member_access, type_object_type |
| 37 | +from mypy.checkmember import analyze_member_access, type_object_type, bind_self |
37 | 38 | from mypy.constraints import get_actual_type
|
38 | 39 | from mypy.checkstrformat import StringFormatterChecker
|
39 |
| -from mypy.expandtype import expand_type |
| 40 | +from mypy.expandtype import expand_type, expand_type_by_instance |
40 | 41 | from mypy.util import split_module_names
|
41 | 42 | from mypy.semanal import fill_typevars
|
42 | 43 |
|
@@ -983,10 +984,69 @@ def analyze_ordinary_member_access(self, e: MemberExpr,
|
983 | 984 | else:
|
984 | 985 | # This is a reference to a non-module attribute.
|
985 | 986 | original_type = self.accept(e.expr)
|
986 |
| - return analyze_member_access(e.name, original_type, e, |
987 |
| - is_lvalue, False, False, |
988 |
| - self.named_type, self.not_ready_callback, self.msg, |
989 |
| - original_type=original_type, chk=self.chk) |
| 987 | + member_type = analyze_member_access( |
| 988 | + e.name, original_type, e, is_lvalue, False, False, |
| 989 | + self.named_type, self.not_ready_callback, self.msg, |
| 990 | + original_type=original_type, chk=self.chk) |
| 991 | + if is_lvalue: |
| 992 | + return member_type |
| 993 | + else: |
| 994 | + return self.analyze_descriptor_access(original_type, member_type, e) |
| 995 | + |
| 996 | + def analyze_descriptor_access(self, instance_type: Type, descriptor_type: Type, |
| 997 | + context: Context) -> Type: |
| 998 | + """Type check descriptor access. |
| 999 | +
|
| 1000 | + Arguments: |
| 1001 | + instance_type: The type of the instance on which the descriptor |
| 1002 | + attribute is being accessed (the type of ``a`` in ``a.f`` when |
| 1003 | + ``f`` is a descriptor). |
| 1004 | + descriptor_type: The type of the descriptor attribute being accessed |
| 1005 | + (the type of ``f`` in ``a.f`` when ``f`` is a descriptor). |
| 1006 | + context: The node defining the context of this inference. |
| 1007 | + Return: |
| 1008 | + The return type of the appropriate ``__get__`` overload for the descriptor. |
| 1009 | + """ |
| 1010 | + if not isinstance(descriptor_type, Instance): |
| 1011 | + return descriptor_type |
| 1012 | + |
| 1013 | + if not descriptor_type.type.has_readable_member('__get__'): |
| 1014 | + return descriptor_type |
| 1015 | + |
| 1016 | + dunder_get = descriptor_type.type.get_method('__get__') |
| 1017 | + |
| 1018 | + if dunder_get is None: |
| 1019 | + self.msg.fail("{}.__get__ is not callable".format(descriptor_type), context) |
| 1020 | + return AnyType() |
| 1021 | + |
| 1022 | + function = function_type(dunder_get, self.named_type('builtins.function')) |
| 1023 | + bound_method = bind_self(function, descriptor_type) |
| 1024 | + typ = map_instance_to_supertype(descriptor_type, dunder_get.info) |
| 1025 | + dunder_get_type = expand_type_by_instance(bound_method, typ) |
| 1026 | + owner_type = None # type: Type |
| 1027 | + |
| 1028 | + if isinstance(instance_type, FunctionLike) and instance_type.is_type_obj(): |
| 1029 | + owner_type = instance_type.items()[0].ret_type |
| 1030 | + instance_type = NoneTyp() |
| 1031 | + elif isinstance(instance_type, TypeType): |
| 1032 | + owner_type = instance_type.item |
| 1033 | + instance_type = NoneTyp() |
| 1034 | + else: |
| 1035 | + owner_type = instance_type |
| 1036 | + |
| 1037 | + _, inferred_dunder_get_type = self.check_call( |
| 1038 | + dunder_get_type, [TempNode(instance_type), TempNode(TypeType(owner_type))], |
| 1039 | + [nodes.ARG_POS, nodes.ARG_POS], context) |
| 1040 | + |
| 1041 | + if isinstance(inferred_dunder_get_type, AnyType): |
| 1042 | + # check_call failed, and will have reported an error |
| 1043 | + return inferred_dunder_get_type |
| 1044 | + |
| 1045 | + if not isinstance(inferred_dunder_get_type, CallableType): |
| 1046 | + self.msg.fail("{}.__get__ is not callable".format(descriptor_type), context) |
| 1047 | + return AnyType() |
| 1048 | + |
| 1049 | + return inferred_dunder_get_type.ret_type |
990 | 1050 |
|
991 | 1051 | def analyze_external_member_access(self, member: str, base_type: Type,
|
992 | 1052 | context: Context) -> Type:
|
|
0 commit comments