@@ -145,37 +145,60 @@ def test_constructor_positional(self):
145145 self .assertRaises (TypeError , self ._new_future , 42 )
146146
147147 def test_uninitialized (self ):
148+ # Test that C Future doesn't crash when Future.__init__()
149+ # call was skipped.
150+
148151 fut = self .cls .__new__ (self .cls , loop = self .loop )
149152 self .assertRaises (asyncio .InvalidStateError , fut .result )
153+
150154 fut = self .cls .__new__ (self .cls , loop = self .loop )
151155 self .assertRaises (asyncio .InvalidStateError , fut .exception )
156+
152157 fut = self .cls .__new__ (self .cls , loop = self .loop )
153158 with self .assertRaises ((RuntimeError , AttributeError )):
154159 fut .set_result (None )
160+
155161 fut = self .cls .__new__ (self .cls , loop = self .loop )
156162 with self .assertRaises ((RuntimeError , AttributeError )):
157163 fut .set_exception (Exception )
164+
158165 fut = self .cls .__new__ (self .cls , loop = self .loop )
159166 with self .assertRaises ((RuntimeError , AttributeError )):
160167 fut .cancel ()
168+
161169 fut = self .cls .__new__ (self .cls , loop = self .loop )
162170 with self .assertRaises ((RuntimeError , AttributeError )):
163171 fut .add_done_callback (lambda f : None )
172+
164173 fut = self .cls .__new__ (self .cls , loop = self .loop )
165174 with self .assertRaises ((RuntimeError , AttributeError )):
166175 fut .remove_done_callback (lambda f : None )
176+
167177 fut = self .cls .__new__ (self .cls , loop = self .loop )
168178 with self .assertRaises ((RuntimeError , AttributeError )):
169179 fut ._schedule_callbacks ()
180+
170181 fut = self .cls .__new__ (self .cls , loop = self .loop )
171182 try :
172183 repr (fut )
173- except AttributeError :
184+ except (RuntimeError , AttributeError ):
185+ pass
186+
187+ fut = self .cls .__new__ (self .cls , loop = self .loop )
188+ try :
189+ fut .__await__ ()
190+ except RuntimeError :
191+ pass
192+
193+ fut = self .cls .__new__ (self .cls , loop = self .loop )
194+ try :
195+ iter (fut )
196+ except RuntimeError :
174197 pass
198+
175199 fut = self .cls .__new__ (self .cls , loop = self .loop )
176- fut .cancelled ()
177- fut .done ()
178- iter (fut )
200+ self .assertFalse (fut .cancelled ())
201+ self .assertFalse (fut .done ())
179202
180203 def test_cancel (self ):
181204 f = self ._new_future (loop = self .loop )
@@ -246,30 +269,32 @@ def test_future_repr(self):
246269 self .loop .set_debug (True )
247270 f_pending_debug = self ._new_future (loop = self .loop )
248271 frame = f_pending_debug ._source_traceback [- 1 ]
249- self .assertEqual (repr ( f_pending_debug ),
250- '<Future pending created at %s:%s>'
251- % ( frame [0 ], frame [1 ]) )
272+ self .assertEqual (
273+ repr ( f_pending_debug ),
274+ f'< { self . cls . __name__ } pending created at { frame [0 ]} : { frame [1 ]} >' )
252275 f_pending_debug .cancel ()
253276
254277 self .loop .set_debug (False )
255278 f_pending = self ._new_future (loop = self .loop )
256- self .assertEqual (repr (f_pending ), '<Future pending>' )
279+ self .assertEqual (repr (f_pending ), f'< { self . cls . __name__ } pending>' )
257280 f_pending .cancel ()
258281
259282 f_cancelled = self ._new_future (loop = self .loop )
260283 f_cancelled .cancel ()
261- self .assertEqual (repr (f_cancelled ), '<Future cancelled>' )
284+ self .assertEqual (repr (f_cancelled ), f'< { self . cls . __name__ } cancelled>' )
262285
263286 f_result = self ._new_future (loop = self .loop )
264287 f_result .set_result (4 )
265- self .assertEqual (repr (f_result ), '<Future finished result=4>' )
288+ self .assertEqual (
289+ repr (f_result ), f'<{ self .cls .__name__ } finished result=4>' )
266290 self .assertEqual (f_result .result (), 4 )
267291
268292 exc = RuntimeError ()
269293 f_exception = self ._new_future (loop = self .loop )
270294 f_exception .set_exception (exc )
271- self .assertEqual (repr (f_exception ),
272- '<Future finished exception=RuntimeError()>' )
295+ self .assertEqual (
296+ repr (f_exception ),
297+ f'<{ self .cls .__name__ } finished exception=RuntimeError()>' )
273298 self .assertIs (f_exception .exception (), exc )
274299
275300 def func_repr (func ):
@@ -280,19 +305,20 @@ def func_repr(func):
280305 f_one_callbacks = self ._new_future (loop = self .loop )
281306 f_one_callbacks .add_done_callback (_fakefunc )
282307 fake_repr = func_repr (_fakefunc )
283- self .assertRegex (repr (f_one_callbacks ),
284- r'<Future pending cb=\[%s\]>' % fake_repr )
308+ self .assertRegex (
309+ repr (f_one_callbacks ),
310+ r'<' + self .cls .__name__ + r' pending cb=\[%s\]>' % fake_repr )
285311 f_one_callbacks .cancel ()
286312 self .assertEqual (repr (f_one_callbacks ),
287- '<Future cancelled>' )
313+ f'< { self . cls . __name__ } cancelled>' )
288314
289315 f_two_callbacks = self ._new_future (loop = self .loop )
290316 f_two_callbacks .add_done_callback (first_cb )
291317 f_two_callbacks .add_done_callback (last_cb )
292318 first_repr = func_repr (first_cb )
293319 last_repr = func_repr (last_cb )
294320 self .assertRegex (repr (f_two_callbacks ),
295- r'<Future pending cb=\[%s, %s\]>'
321+ r'<' + self . cls . __name__ + r' pending cb=\[%s, %s\]>'
296322 % (first_repr , last_repr ))
297323
298324 f_many_callbacks = self ._new_future (loop = self .loop )
@@ -301,11 +327,12 @@ def func_repr(func):
301327 f_many_callbacks .add_done_callback (_fakefunc )
302328 f_many_callbacks .add_done_callback (last_cb )
303329 cb_regex = r'%s, <8 more>, %s' % (first_repr , last_repr )
304- self .assertRegex (repr (f_many_callbacks ),
305- r'<Future pending cb=\[%s\]>' % cb_regex )
330+ self .assertRegex (
331+ repr (f_many_callbacks ),
332+ r'<' + self .cls .__name__ + r' pending cb=\[%s\]>' % cb_regex )
306333 f_many_callbacks .cancel ()
307334 self .assertEqual (repr (f_many_callbacks ),
308- '<Future cancelled>' )
335+ f'< { self . cls . __name__ } cancelled>' )
309336
310337 def test_copy_state (self ):
311338 from asyncio .futures import _copy_future_state
@@ -475,7 +502,7 @@ def memory_error():
475502 support .gc_collect ()
476503
477504 if sys .version_info >= (3 , 4 ):
478- regex = r'^Future exception was never retrieved\n'
505+ regex = f'^ { self . cls . __name__ } exception was never retrieved\n '
479506 exc_info = (type (exc ), exc , exc .__traceback__ )
480507 m_log .error .assert_called_once_with (mock .ANY , exc_info = exc_info )
481508 else :
@@ -531,7 +558,16 @@ def __del__(self):
531558@unittest .skipUnless (hasattr (futures , '_CFuture' ),
532559 'requires the C _asyncio module' )
533560class CFutureTests (BaseFutureTests , test_utils .TestCase ):
534- cls = getattr (futures , '_CFuture' )
561+ cls = futures ._CFuture
562+
563+
564+ @unittest .skipUnless (hasattr (futures , '_CFuture' ),
565+ 'requires the C _asyncio module' )
566+ class CSubFutureTests (BaseFutureTests , test_utils .TestCase ):
567+ class CSubFuture (futures ._CFuture ):
568+ pass
569+
570+ cls = CSubFuture
535571
536572
537573class PyFutureTests (BaseFutureTests , test_utils .TestCase ):
@@ -556,6 +592,76 @@ def bag_appender(future):
556592 def _new_future (self ):
557593 raise NotImplementedError
558594
595+ def test_callbacks_remove_first_callback (self ):
596+ bag = []
597+ f = self ._new_future ()
598+
599+ cb1 = self ._make_callback (bag , 42 )
600+ cb2 = self ._make_callback (bag , 17 )
601+ cb3 = self ._make_callback (bag , 100 )
602+
603+ f .add_done_callback (cb1 )
604+ f .add_done_callback (cb2 )
605+ f .add_done_callback (cb3 )
606+
607+ f .remove_done_callback (cb1 )
608+ f .remove_done_callback (cb1 )
609+
610+ self .assertEqual (bag , [])
611+ f .set_result ('foo' )
612+
613+ self .run_briefly ()
614+
615+ self .assertEqual (bag , [17 , 100 ])
616+ self .assertEqual (f .result (), 'foo' )
617+
618+ def test_callbacks_remove_first_and_second_callback (self ):
619+ bag = []
620+ f = self ._new_future ()
621+
622+ cb1 = self ._make_callback (bag , 42 )
623+ cb2 = self ._make_callback (bag , 17 )
624+ cb3 = self ._make_callback (bag , 100 )
625+
626+ f .add_done_callback (cb1 )
627+ f .add_done_callback (cb2 )
628+ f .add_done_callback (cb3 )
629+
630+ f .remove_done_callback (cb1 )
631+ f .remove_done_callback (cb2 )
632+ f .remove_done_callback (cb1 )
633+
634+ self .assertEqual (bag , [])
635+ f .set_result ('foo' )
636+
637+ self .run_briefly ()
638+
639+ self .assertEqual (bag , [100 ])
640+ self .assertEqual (f .result (), 'foo' )
641+
642+ def test_callbacks_remove_third_callback (self ):
643+ bag = []
644+ f = self ._new_future ()
645+
646+ cb1 = self ._make_callback (bag , 42 )
647+ cb2 = self ._make_callback (bag , 17 )
648+ cb3 = self ._make_callback (bag , 100 )
649+
650+ f .add_done_callback (cb1 )
651+ f .add_done_callback (cb2 )
652+ f .add_done_callback (cb3 )
653+
654+ f .remove_done_callback (cb3 )
655+ f .remove_done_callback (cb3 )
656+
657+ self .assertEqual (bag , [])
658+ f .set_result ('foo' )
659+
660+ self .run_briefly ()
661+
662+ self .assertEqual (bag , [42 , 17 ])
663+ self .assertEqual (f .result (), 'foo' )
664+
559665 def test_callbacks_invoked_on_set_result (self ):
560666 bag = []
561667 f = self ._new_future ()
@@ -678,6 +784,17 @@ def _new_future(self):
678784 return futures ._CFuture (loop = self .loop )
679785
680786
787+ @unittest .skipUnless (hasattr (futures , '_CFuture' ),
788+ 'requires the C _asyncio module' )
789+ class CSubFutureDoneCallbackTests (BaseFutureDoneCallbackTests ,
790+ test_utils .TestCase ):
791+
792+ def _new_future (self ):
793+ class CSubFuture (futures ._CFuture ):
794+ pass
795+ return CSubFuture (loop = self .loop )
796+
797+
681798class PyFutureDoneCallbackTests (BaseFutureDoneCallbackTests ,
682799 test_utils .TestCase ):
683800
0 commit comments