@@ -125,9 +125,45 @@ def do_prefix_binops():
125125 else :
126126 print '=' , format_result (x )
127127
128+ # New-style class version of CoerceNumber
129+ class CoerceTo (object ):
130+ def __init__ (self , arg ):
131+ self .arg = arg
132+ def __coerce__ (self , other ):
133+ if isinstance (other , CoerceTo ):
134+ return self .arg , other .arg
135+ else :
136+ return self .arg , other
137+
138+ def assert_ (expr , msg = None ):
139+ if not expr :
140+ raise AssertionError , msg
141+
142+ def do_cmptypes ():
143+ # Built-in tp_compare slots expect their arguments to have the
144+ # same type, but a user-defined __coerce__ doesn't have to obey.
145+ # SF #980352
146+ evil_coercer = CoerceTo (42 )
147+ # Make sure these don't crash any more
148+ assert_ (cmp (u'fish' , evil_coercer ) != 0 )
149+ assert_ (cmp (slice (1 ), evil_coercer ) != 0 )
150+ # ...but that this still works
151+ class WackyComparer (object ):
152+ def __cmp__ (self , other ):
153+ assert_ (other == 42 , 'expected evil_coercer, got %r' % other )
154+ return 0
155+ assert_ (cmp (WackyComparer (), evil_coercer ) == 0 )
156+ # ...and classic classes too, since that code path is a little different
157+ class ClassicWackyComparer :
158+ def __cmp__ (self , other ):
159+ assert_ (other == 42 , 'expected evil_coercer, got %r' % other )
160+ return 0
161+ assert_ (cmp (ClassicWackyComparer (), evil_coercer ) == 0 )
162+
128163warnings .filterwarnings ("ignore" ,
129164 r'complex divmod\(\), // and % are deprecated' ,
130165 DeprecationWarning ,
131166 r'test.test_coercion$' )
132167do_infix_binops ()
133168do_prefix_binops ()
169+ do_cmptypes ()
0 commit comments