Skip to content

Commit e2178b8

Browse files
authored
Now print >> is properly checked (#11576)
1 parent cc6c8a9 commit e2178b8

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

mypy/checker.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3938,8 +3938,21 @@ def visit_print_stmt(self, s: PrintStmt) -> None:
39383938
if s.target:
39393939
target_type = get_proper_type(self.expr_checker.accept(s.target))
39403940
if not isinstance(target_type, NoneType):
3941-
# TODO: Also verify the type of 'write'.
3942-
self.expr_checker.analyze_external_member_access('write', target_type, s.target)
3941+
write_type = self.expr_checker.analyze_external_member_access(
3942+
'write', target_type, s.target)
3943+
required_type = CallableType(
3944+
arg_types=[self.named_type('builtins.str')],
3945+
arg_kinds=[ARG_POS],
3946+
arg_names=[None],
3947+
ret_type=AnyType(TypeOfAny.implementation_artifact),
3948+
fallback=self.named_type('builtins.function'),
3949+
)
3950+
# This has to be hard-coded, since it is a syntax pattern, not a function call.
3951+
if not is_subtype(write_type, required_type):
3952+
self.fail(message_registry.PYTHON2_PRINT_FILE_TYPE.format(
3953+
write_type,
3954+
required_type,
3955+
), s.target)
39433956

39443957
def visit_break_stmt(self, s: BreakStmt) -> None:
39453958
self.binder.handle_break()

mypy/message_registry.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
135135
code=codes.TRUTHY_BOOL,
136136
)
137137
NOT_CALLABLE: Final = '{} not callable'
138+
PYTHON2_PRINT_FILE_TYPE: Final = (
139+
'Argument "file" to "print" has incompatible type "{}"; expected "{}"'
140+
)
138141

139142
# Generic
140143
GENERIC_INSTANCE_VAR_CLASS_ACCESS: Final = (

test-data/unit/check-python2.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,16 @@ class A:
2727
# type: (str) -> None
2828
pass
2929

30+
class B:
31+
def write(self):
32+
# type: () -> int
33+
pass
34+
3035
print >>A(), ''
3136
print >>None, ''
3237
print >>1, '' # E: "int" has no attribute "write"
3338
print >>(None + ''), None # E: Unsupported left operand type for + ("None")
39+
print >> B(), '' # E: Argument "file" to "print" has incompatible type "def () -> builtins.int"; expected "def (builtins.str) -> Any"
3440

3541
[case testDivision]
3642
class A:

0 commit comments

Comments
 (0)