Skip to content

Commit 3ce8d6a

Browse files
committed
Disallow assignments to methods
1 parent 3c13419 commit 3ce8d6a

File tree

4 files changed

+28
-9
lines changed

4 files changed

+28
-9
lines changed

mypy/checker.py

+8
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,14 @@ def infer_local_variable_type(self, x, y, z):
396396
lvalue_types.append(None)
397397
index_lvalues.append(ilv)
398398
inferred.append(None)
399+
elif isinstance(lv, MemberExpr):
400+
mlv = (MemberExpr)lv
401+
lvalue_types.append(
402+
self.expr_checker.analyse_ordinary_member_access(mlv,
403+
True))
404+
self.store_type(mlv, lvalue_types[-1])
405+
index_lvalues.append(None)
406+
inferred.append(None)
399407
else:
400408
lvalue_types.append(self.accept(lv))
401409
index_lvalues.append(None)

mypy/checkmember.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,10 @@
4343
info = override_info
4444

4545
# Look up the member. First look up the method dictionary.
46-
FuncBase method = None
47-
if not is_lvalue:
48-
method = info.get_method(name)
49-
46+
method = info.get_method(name)
5047
if method:
51-
# Found a method. The call below has a unique result for all valid
52-
# programs.
48+
if is_lvalue:
49+
msg.fail(messages.CANNOT_ASSIGN_TO_METHOD, node)
5350
itype = map_instance_to_supertype(itype, method.info)
5451
return expand_type_by_instance(method_type(method), itype)
5552
else:
@@ -65,10 +62,11 @@
6562
return analyse_member_access(name, basic_types.tuple, node, is_lvalue,
6663
is_super, basic_types, msg)
6764
elif isinstance(typ, FunctionLike) and ((FunctionLike)typ).is_type_obj():
68-
# TODO super? lvalue?
65+
# TODO super?
6966
sig = (FunctionLike)typ
7067
itype = (Instance)sig.items()[0].ret_type
71-
result = analyse_class_attribute_access(itype, name, node, msg)
68+
result = analyse_class_attribute_access(itype, name, node, is_lvalue,
69+
msg)
7270
if result:
7371
return result
7472
# Look up from the 'type' type.
@@ -148,9 +146,11 @@
148146

149147

150148
Type analyse_class_attribute_access(Instance itype, str name, Context context,
151-
MessageBuilder msg):
149+
bool is_lvalue, MessageBuilder msg):
152150
node = itype.type.get(name)
153151
if node:
152+
if is_lvalue and isinstance(node.node, FuncDef):
153+
msg.fail(messages.CANNOT_ASSIGN_TO_METHOD, context)
154154
t = node.type()
155155
if t:
156156
return add_class_tvars(t, itype.type)

mypy/messages.py

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
INVALID_SLICE_INDEX = 'Slice index must be an integer or None'
4848
CANNOT_INFER_LAMBDA_TYPE = 'Cannot infer type of lambda'
4949
CANNOT_ACCESS_INIT = 'Cannot access "__init__" directly'
50+
CANNOT_ASSIGN_TO_METHOD = 'Cannot assign to a method'
5051

5152

5253
class MessageBuilder:

test/data/check-classes.test

+10
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ main: In member "f" of class "A":
104104
main, line 3: Incompatible types in assignment
105105
main, line 4: "A" has no attribute "g"
106106

107+
[case testAssignToMethodViaInstance]
108+
class A:
109+
def f(self): pass
110+
A().f = None # E: Cannot assign to a method
111+
107112

108113
-- Attributes
109114
-- ----------
@@ -460,6 +465,11 @@ A.__init__(a, b)
460465
A.__init__(b, b) # E: Argument 1 to "__init__" of "A" has incompatible type "B"
461466
A.__init__(a, a) # E: Argument 2 to "__init__" of "A" has incompatible type "A"
462467

468+
[case testAssignToMethodViaClass]
469+
class A:
470+
def f(self): pass
471+
A.f = None # E: Cannot assign to a method
472+
463473

464474
-- Nested classes
465475
-- --------------

0 commit comments

Comments
 (0)