Skip to content

gh-108518: Make concurrent.futures.Executor.map() consistent with built-in map() #109497

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

xzmeng
Copy link
Contributor

@xzmeng xzmeng commented Sep 16, 2023

The current behavior of concurrent.futures.Executor.map() is not consistent with documentation:

Similar to map(func, *iterables) except:

  • the iterables are collected immediately rather than lazily;
  • func is executed asynchronously and several calls to func may be made concurrently.

When a next call raises an Exception, you can not get any subsequent Future result, even if the computation has completed and returns a valid result.

If it is expected or don't consider to make changes to this behavior, I'd like to make a PR to update the relevant docs.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good code, but slightly over-engineered. I think that it will be better after simplification.

And there is a regression (removing the close() method).

@@ -566,6 +568,46 @@ def set_exception(self, exception):

__class_getitem__ = classmethod(types.GenericAlias)


class _FutureResult(object):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks over-engineered. Why not simply use a result-exception tuple?

Comment on lines 703 to 705
@classmethod
def from_generator(cls, gen):
return cls(gen)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is redundant. You can simply use constructor.

@@ -648,6 +695,25 @@ def __exit__(self, exc_type, exc_val, exc_tb):
return False


class _MapResultIterator(object):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(object) is redundant.

Current iterator has the close() method, calling which cancels all futures. It is a useful feature. Please add a close() method in a new class. I do not think that it is worth to implement send() and throw(), they are not so useful and are rather an implementation detail.

) as gen:
with self.assertRaises(TimeoutError):
next(gen)
iterator = pool.map(log_n_wait, ["second"], timeout=0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code should continue to work without changes.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I resolved conflicts, fixed some errors, simplified code, added the close method, added docs and more tests. TimeoutError immediately cancels all calls.

Now this PR LGTM.

@pitrou, @brianquinlan, @vstinner, @gpshead, could anybody of your please take a look at this if you have a time?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy