diff -r ede0a342ed7e Lib/concurrent/futures/_base.py --- a/Lib/concurrent/futures/_base.py Thu Jan 23 16:49:22 2014 +0200 +++ b/Lib/concurrent/futures/_base.py Fri Jan 24 12:15:14 2014 -0500 @@ -181,7 +181,8 @@ Returns: An iterator that yields the given Futures as they complete (finished or - cancelled). + cancelled). If any given Futures are duplicated, they will be returned + once. Raises: TimeoutError: If the entire result iterator could not be generated @@ -190,11 +191,12 @@ if timeout is not None: end_time = timeout + time.time() + fs = set(fs) with _AcquireFutures(fs): finished = set( f for f in fs if f._state in [CANCELLED_AND_NOTIFIED, FINISHED]) - pending = set(fs) - finished + pending = fs - finished waiter = _create_and_install_waiters(fs, _AS_COMPLETED) try: diff -r ede0a342ed7e Lib/test/test_concurrent_futures.py --- a/Lib/test/test_concurrent_futures.py Thu Jan 23 16:49:22 2014 +0200 +++ b/Lib/test/test_concurrent_futures.py Fri Jan 24 12:15:14 2014 -0500 @@ -350,6 +350,13 @@ SUCCESSFUL_FUTURE]), completed_futures) + def test_duplicate_futures(self): + # Issue 20367. Duplicate futures should not raise exceptions or give + # duplicate responses. + future1 = self.executor.submit(time.sleep, 2) + completed = [f for f in futures.as_completed([future1,future1])] + self.assertEqual(len(completed), 1) + class ThreadPoolAsCompletedTests(ThreadPoolMixin, AsCompletedTests, unittest.TestCase): pass