13
13
from collections import OrderedDict
14
14
import re
15
15
import difflib
16
+ from textwrap import dedent
16
17
17
18
from typing import cast , List , Dict , Any , Sequence , Iterable , Tuple , Set , Optional , Union
18
19
@@ -187,7 +188,7 @@ def report(self, msg: str, context: Optional[Context], severity: str,
187
188
if self .disable_count <= 0 :
188
189
self .errors .report (context .get_line () if context else - 1 ,
189
190
context .get_column () if context else - 1 ,
190
- msg . strip () , severity = severity , file = file , offset = offset ,
191
+ msg , severity = severity , file = file , offset = offset ,
191
192
origin_line = origin .get_line () if origin else None )
192
193
193
194
def fail (self , msg : str , context : Optional [Context ], file : Optional [str ] = None ,
@@ -198,7 +199,15 @@ def fail(self, msg: str, context: Optional[Context], file: Optional[str] = None,
198
199
def note (self , msg : str , context : Context , file : Optional [str ] = None ,
199
200
origin : Optional [Context ] = None , offset : int = 0 ) -> None :
200
201
"""Report a note (unless disabled)."""
201
- self .report (msg , context , 'note' , file = file , origin = origin , offset = offset )
202
+ self .report (msg , context , 'note' , file = file , origin = origin ,
203
+ offset = offset )
204
+
205
+ def note_multiline (self , messages : str , context : Context , file : Optional [str ] = None ,
206
+ origin : Optional [Context ] = None , offset : int = 0 ) -> None :
207
+ """Report as many notes as lines in the message (unless disabled)."""
208
+ for msg in messages .splitlines ():
209
+ self .report (msg , context , 'note' , file = file , origin = origin ,
210
+ offset = offset )
202
211
203
212
def warn (self , msg : str , context : Context , file : Optional [str ] = None ,
204
213
origin : Optional [Context ] = None ) -> None :
@@ -854,12 +863,25 @@ def signature_incompatible_with_supertype(
854
863
name , target ), context )
855
864
856
865
def argument_incompatible_with_supertype (
857
- self , arg_num : int , name : str , name_in_supertype : str ,
858
- supertype : str , context : Context ) -> None :
866
+ self , arg_num : int , name : str , type_name : Optional [ str ] ,
867
+ name_in_supertype : str , supertype : str , context : Context ) -> None :
859
868
target = self .override_target (name , name_in_supertype , supertype )
860
869
self .fail ('Argument {} of "{}" incompatible with {}'
861
870
.format (arg_num , name , target ), context )
862
871
872
+ if name == "__eq__" and type_name :
873
+ multiline_msg = self .comparison_method_example_msg (class_name = type_name )
874
+ self .note_multiline (multiline_msg , context )
875
+
876
+ def comparison_method_example_msg (self , class_name : str ) -> str :
877
+ return dedent ('''\
878
+ It is recommended for "__eq__" to work with arbitrary objects, for example:
879
+ def __eq__(self, other: object) -> bool:
880
+ if not isinstance(other, {class_name}):
881
+ return NotImplemented
882
+ return <logic to compare two {class_name} instances>
883
+ ''' .format (class_name = class_name ))
884
+
863
885
def return_type_incompatible_with_supertype (
864
886
self , name : str , name_in_supertype : str , supertype : str ,
865
887
context : Context ) -> None :
@@ -1110,8 +1132,6 @@ def reveal_locals(self, type_map: Dict[str, Optional[Type]], context: Context) -
1110
1132
# use an ordered dictionary sorted by variable name
1111
1133
sorted_locals = OrderedDict (sorted (type_map .items (), key = lambda t : t [0 ]))
1112
1134
self .fail ("Revealed local types are:" , context )
1113
- # Note that self.fail does a strip() on the message, so we cannot prepend with spaces
1114
- # for indentation
1115
1135
for line in ['{}: {}' .format (k , v ) for k , v in sorted_locals .items ()]:
1116
1136
self .fail (line , context )
1117
1137
0 commit comments