-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
gh-124694: Add concurrent.futures.InterpreterPoolExecutor #124548
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
Changes from 35 commits
5c69d38
01789be
6def4be
84993a5
c540cf0
45d584d
c90c016
1cb4657
4dc0989
57b2db6
75e11d2
69c2b8e
f03c314
4806d9f
efc0395
a29aee3
8bab457
cd29914
0287f3b
80cd7b1
f8d4273
3a8bfce
af6c27a
8c0a405
1ae7ca2
d24e85d
05a03ad
f150931
97d0292
baf0504
5c3a327
a2032a8
54119b8
744dca7
f61d62d
ee65bb2
a7f5c50
b148e09
e365ae7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,9 +15,10 @@ The :mod:`concurrent.futures` module provides a high-level interface for | |||||||||||||||||||||
| asynchronously executing callables. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| The asynchronous execution can be performed with threads, using | ||||||||||||||||||||||
| :class:`ThreadPoolExecutor`, or separate processes, using | ||||||||||||||||||||||
| :class:`ProcessPoolExecutor`. Both implement the same interface, which is | ||||||||||||||||||||||
| defined by the abstract :class:`Executor` class. | ||||||||||||||||||||||
| :class:`ThreadPoolExecutor` or :class:`InterpreterPoolExecutor`, | ||||||||||||||||||||||
| or separate processes, using :class:`ProcessPoolExecutor`. | ||||||||||||||||||||||
| Each implements the same interface, which is defined | ||||||||||||||||||||||
| by the abstract :class:`Executor` class. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. include:: ../includes/wasm-notavail.rst | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -63,7 +64,8 @@ Executor Objects | |||||||||||||||||||||
| setting *chunksize* to a positive integer. For very long iterables, | ||||||||||||||||||||||
| using a large value for *chunksize* can significantly improve | ||||||||||||||||||||||
| performance compared to the default size of 1. With | ||||||||||||||||||||||
| :class:`ThreadPoolExecutor`, *chunksize* has no effect. | ||||||||||||||||||||||
| :class:`ThreadPoolExecutor` and :class:`InterpreterPoolExecutor`, | ||||||||||||||||||||||
| *chunksize* has no effect. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. versionchanged:: 3.5 | ||||||||||||||||||||||
| Added the *chunksize* argument. | ||||||||||||||||||||||
|
|
@@ -227,6 +229,59 @@ ThreadPoolExecutor Example | |||||||||||||||||||||
| print('%r page is %d bytes' % (url, len(data))) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| InterpreterPoolExecutor | ||||||||||||||||||||||
| ----------------------- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| The :class:`InterpreterPoolExecutor` class, a :class:`ThreadPoolExecutor` | ||||||||||||||||||||||
| subclass, uses a pool of isolated interpreters to execute calls | ||||||||||||||||||||||
| asynchronously. Since each interpreter is isolated from the others, | ||||||||||||||||||||||
| side-stepping the :term:`Global Interpreter Lock <global interpreter lock>` , | ||||||||||||||||||||||
| multiple cores can be used. Since Interpreters do not share | ||||||||||||||||||||||
| objects between them, in most cases, only picklable | ||||||||||||||||||||||
| objects can be executed and returned. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. class:: InterpreterPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=(), shared=None) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| A :class:`ThreadPoolExecutor` subclass that executes calls asynchronously | ||||||||||||||||||||||
| using a pool of at most *max_workers* threads. Each thread runs | ||||||||||||||||||||||
| tasks in its own interpreter. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| *initializer* may be a callable and *initargs* a tuple of arguments, | ||||||||||||||||||||||
| just like with :class:`ThreadPoolExecutor`. However, they are pickled | ||||||||||||||||||||||
| like with :class:`ProcessPoolExecutor`. Likewise, functions (and | ||||||||||||||||||||||
| arguments) passed to :meth:`~Executor.submit` are pickled. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. note:: | ||||||||||||||||||||||
| functions defined in the ``__main__`` module cannot be pickled | ||||||||||||||||||||||
| and thus cannot be used. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| *shared* is an optional dict of objects shared by all interpreters | ||||||||||||||||||||||
| in the pool. The items are added to each interpreter's ``__main__`` | ||||||||||||||||||||||
| module. Not all objects are shareable. Those that are include | ||||||||||||||||||||||
| the builtin singletons, :class:`str` and :class:`bytes`, | ||||||||||||||||||||||
| and :class:`memoryview`. See :pep:`734` for more info. | ||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made use of your suggestions. Thanks! |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| You can also pass a script (:class:`str`) for *initializer* or to | ||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps add a brief sentence of why using a script would be desired.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to drop this capability for now.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eric, can we pass a function? We could either pickle it or capture a "path" to it (concatenate I really don't like the aspect of passing code as strings.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it supports passing any callable that can be pickled. Passing a script is an extra capability of InterpreterPoolExecutor.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd remove the code as string capability -- otherwise this all looks good to me!
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll drop it. I can look into adding that capability in separately later.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||||||||||||||||||||||
| :meth:`~Executor.submit` (but not to :meth:`~Executor.map`). | ||||||||||||||||||||||
| In both cases, the script will be executed in the interpreter's | ||||||||||||||||||||||
| ``__main__`` module. The executor will automatically apply | ||||||||||||||||||||||
| :func:`textwrap.dedent` to the script, so you don't have to do so. | ||||||||||||||||||||||
| With a script, arguments must not be passed in. | ||||||||||||||||||||||
| For :meth:`!Executor.submit`, the return value for a script | ||||||||||||||||||||||
| is always ``None``. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Normally an uncaught exception from *initializer* or from a submitted | ||||||||||||||||||||||
| task will be sent back to be raised as is. | ||||||||||||||||||||||
| However, in some situations the exception might not be suitable to be | ||||||||||||||||||||||
| sent back. In that case, the executor raises an | ||||||||||||||||||||||
| :class:`~concurrent.futures.interpreter.ExecutionFailed` exception | ||||||||||||||||||||||
| instead, which contains a summary of the original exception. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Other caveats from parent :class:`ThreadPoolExecutor` apply here. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. versionadded:: next | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ProcessPoolExecutor | ||||||||||||||||||||||
| ------------------- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -574,6 +629,26 @@ Exception classes | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. versionadded:: 3.7 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. currentmodule:: concurrent.futures.interpreter | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. exception:: BrokenInterpreterPool | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Derived from :exc:`~concurrent.futures.thread.BrokenThreadPool`, | ||||||||||||||||||||||
| this exception class is raised when one of the workers | ||||||||||||||||||||||
| of a :class:`~concurrent.futures.InterpreterPoolExecutor` | ||||||||||||||||||||||
| has failed initializing. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. versionadded:: next | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. exception:: ExecutionFailed | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Raised from :class:`~concurrent.futures.InterpreterPoolExecutor` when | ||||||||||||||||||||||
| the given initializer fails or from | ||||||||||||||||||||||
| :meth:`~concurrent.futures.Executor.submit` when there's an uncaught | ||||||||||||||||||||||
| exception from the submitted task. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. versionadded:: next | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. currentmodule:: concurrent.futures.process | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| .. exception:: BrokenProcessPool | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This motivated me to add a bit more clarity in the docs. 😄