Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 50 additions & 35 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,15 +870,13 @@ def analyze_var(
mx.msg.read_only_property(name, itype.type, mx.context)
if var.is_classvar:
mx.msg.cant_assign_to_classvar(name, mx.context)
t = freshen_all_functions_type_vars(typ)
t = expand_self_type_if_needed(t, mx, var, original_itype)
t = expand_type_by_instance(t, itype)
freeze_all_type_vars(t)
result = t
typ = get_proper_type(typ)
# This is the most common case for variables, so start with this.
result = expand_without_binding(typ, var, itype, original_itype, mx)

# A non-None value indicates that we should actually bind self for this variable.
call_type: ProperType | None = None
if var.is_initialized_in_class and (not is_instance_var(var) or mx.is_operator):
typ = get_proper_type(typ)
if isinstance(typ, FunctionLike) and not typ.is_type_obj():
call_type = typ
elif var.is_property:
Expand All @@ -888,37 +886,23 @@ def analyze_var(
else:
call_type = typ

# Bound variables with callable types are treated like methods
# (these are usually method aliases like __rmul__ = __mul__).
if isinstance(call_type, FunctionLike) and not call_type.is_type_obj():
if mx.is_lvalue and not mx.suppress_errors:
if var.is_property and not var.is_settable_property:
mx.msg.read_only_property(name, itype.type, mx.context)
elif not var.is_property:
mx.msg.cant_assign_to_method(mx.context)

if not var.is_staticmethod:
# Class-level function objects and classmethods become bound methods:
# the former to the instance, the latter to the class.
functype: FunctionLike = call_type
signature = freshen_all_functions_type_vars(functype)
bound = get_proper_type(expand_self_type(var, signature, mx.original_type))
assert isinstance(bound, FunctionLike)
signature = bound
signature = check_self_arg(
signature, mx.self_type, var.is_classmethod, mx.context, name, mx.msg
)
signature = bind_self(signature, mx.self_type, var.is_classmethod)
expanded_signature = expand_type_by_instance(signature, itype)
freeze_all_type_vars(expanded_signature)
if var.is_property:
# A property cannot have an overloaded type => the cast is fine.
assert isinstance(expanded_signature, CallableType)
if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
# TODO: use check_call() to infer better type, same as for __set__().
result = expanded_signature.arg_types[0]
else:
result = expanded_signature.ret_type
if mx.is_lvalue and not var.is_property and not mx.suppress_errors:
mx.msg.cant_assign_to_method(mx.context)

# Bind the self type for each callable component (when needed).
if call_type and not var.is_staticmethod:
bound_items = []
for ct in call_type.items if isinstance(call_type, UnionType) else [call_type]:
p_ct = get_proper_type(ct)
if isinstance(p_ct, FunctionLike) and not p_ct.is_type_obj():
item = expand_and_bind_callable(p_ct, var, itype, name, mx)
else:
result = expanded_signature
item = expand_without_binding(ct, var, itype, original_itype, mx)
bound_items.append(item)
result = UnionType.make_union(bound_items)
else:
if not var.is_ready and not mx.no_deferral:
mx.not_ready_callback(var.name, mx.context)
Expand All @@ -937,6 +921,37 @@ def analyze_var(
return result


def expand_without_binding(
typ: Type, var: Var, itype: Instance, original_itype: Instance, mx: MemberContext
) -> Type:
typ = freshen_all_functions_type_vars(typ)
typ = expand_self_type_if_needed(typ, mx, var, original_itype)
expanded = expand_type_by_instance(typ, itype)
freeze_all_type_vars(expanded)
return expanded


def expand_and_bind_callable(
functype: FunctionLike, var: Var, itype: Instance, name: str, mx: MemberContext
) -> Type:
functype = freshen_all_functions_type_vars(functype)
typ = get_proper_type(expand_self_type(var, functype, mx.original_type))
assert isinstance(typ, FunctionLike)
typ = check_self_arg(typ, mx.self_type, var.is_classmethod, mx.context, name, mx.msg)
typ = bind_self(typ, mx.self_type, var.is_classmethod)
expanded = expand_type_by_instance(typ, itype)
freeze_all_type_vars(expanded)
if not var.is_property:
return expanded
# TODO: a decorated property can result in Overloaded here.
assert isinstance(expanded, CallableType)
if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
# TODO: use check_call() to infer better type, same as for __set__().
return expanded.arg_types[0]
else:
return expanded.ret_type


def expand_self_type_if_needed(
t: Type, mx: MemberContext, var: Var, itype: Instance, is_class: bool = False
) -> Type:
Expand Down
9 changes: 9 additions & 0 deletions test-data/unit/check-classvar.test
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,12 @@ class C:
c:C
c.foo() # E: Too few arguments \
# N: "foo" is considered instance variable, to make it class variable use ClassVar[...]

[case testClassVarUnionBoundOnInstance]
from typing import Union, Callable, ClassVar

class C:
def f(self) -> int: ...
g: ClassVar[Union[Callable[[C], int], int]] = f

reveal_type(C().g) # N: Revealed type is "Union[def () -> builtins.int, builtins.int]"