From 3faaa8857a42a36383bb18425444e597fc876797 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 14 Sep 2018 13:32:07 -0700 Subject: [PATCH] bpo-33649: Refresh Tasks and Futures pages (#9314) * bpo-33649: Refresh Tasks and Futures pages * Fixes * Fix markup --- Doc/library/asyncio-eventloop.rst | 2 +- Doc/library/asyncio-future.rst | 240 ++++++ Doc/library/asyncio-protocol.rst | 18 +- Doc/library/asyncio-task.rst | 1295 +++++++++++++---------------- Doc/library/asyncio.rst | 51 +- 5 files changed, 885 insertions(+), 721 deletions(-) create mode 100644 Doc/library/asyncio-future.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index baa5234d3e5..e1b47d2588c 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1429,7 +1429,7 @@ event loop:: .. seealso:: - A similar :ref:`Hello World ` + A similar :ref:`Hello World ` example created with a coroutine and the :func:`run` function. diff --git a/Doc/library/asyncio-future.rst b/Doc/library/asyncio-future.rst new file mode 100644 index 00000000000..2c41c1c2165 --- /dev/null +++ b/Doc/library/asyncio-future.rst @@ -0,0 +1,240 @@ +.. currentmodule:: asyncio + + +======= +Futures +======= + +*Future* objects are used to bridge low-level callback-based code +with high-level async/await code. + + +Future Functions +================ + +.. function:: isfuture(obj) + + Return ``True`` if *obj* is either of: + + * an instance of :class:`asyncio.Future`, + * an instance of :class:`asyncio.Task`, + * a Future-like object with a ``_asyncio_future_blocking`` + attribute. + + .. versionadded:: 3.5 + + +.. function:: ensure_future(obj, \*, loop=None) + + Return: + + * *obj* argument as is, if *obj* is a :class:`Future`, + a :class:`Task`, or a Future-like object (:func:`isfuture` + is used for the test.) + + * a :class:`Task` object wrapping *obj*, if *obj* is a + coroutine (:func:`iscoroutine` is used for the test.) + + * a :class:`Task` object that would await on *obj*, if *obj* is an + awaitable (:func:`inspect.isawaitable` is used for the test.) + + If *obj* is neither of the above a :exc:`TypeError` is raised. + + .. important:: + + See also the :func:`create_task` function which is the + preferred way for creating new Tasks. + + .. versionchanged:: 3.5.1 + The function accepts any :term:`awaitable` object. + + +.. function:: wrap_future(future, \*, loop=None) + + Wrap a :class:`concurrent.futures.Future` object in a + :class:`asyncio.Future` object. + + +Future Object +============= + +.. class:: Future(\*, loop=None) + + A Future represents an eventual result of an asynchronous + operation. Not thread-safe. + + Future is an :term:`awaitable` object. Coroutines can await on + Future objects until they either have a result or an exception + set, or until they are cancelled. + + Typically Futures are used to enable low-level + callback-based code (e.g. in protocols implemented using asyncio + :ref:`transports `) + to interoperate with high-level async/await code. + + The rule of thumb is to never expose Future objects in user-facing + APIs, and the recommended way to create a Future object is to call + :meth:`loop.create_future`. This way alternative event loop + implementations can inject their own optimized implementations + of a Future object. + + .. versionchanged:: 3.7 + Added support for the :mod:`contextvars` module. + + .. method:: result() + + Return the result of the Future. + + If the Future is *done* and has a result set by the + :meth:`set_result` method, the result value is returned. + + If the Future is *done* and has an exception set by the + :meth:`set_exception` method, this method raises the exception. + + If the Future has been *cancelled*, this method raises + a :exc:`CancelledError` exception. + + If the Future's result isn't yet available, this method raises + a :exc:`InvalidStateError` exception. + + .. method:: set_result(result) + + Mark the Future as *done* and set its result. + + Raises a :exc:`InvalidStateError` error if the Future is + already *done*. + + .. method:: set_exception(exception) + + Mark the Future as *done* and set an exception. + + Raises a :exc:`InvalidStateError` error if the Future is + already *done*. + + .. method:: done() + + Return ``True`` if the Future is *done*. + + A Future is *done* if it was *cancelled* or if it has a result + or an exception set with :meth:`set_result` or + :meth:`set_exception` calls. + + .. method:: add_done_callback(callback, *, context=None) + + Add a callback to be run when the Future is *done*. + + The *callback* is called with the Future object as its only + argument. + + If the Future is already *done* when this method is called, + the callback is scheduled with :meth:`loop.call_soon`. + + An optional keyword-only *context* argument allows specifying a + custom :class:`contextvars.Context` for the *callback* to run in. + The current context is used when no *context* is provided. + + :func:`functools.partial` can be used to pass parameters + to the callback, e.g.:: + + # Call 'print("Future:", fut)' when "fut" is done. + fut.add_done_callback( + functools.partial(print, "Future:")) + + .. versionchanged:: 3.7 + The *context* keyword-only parameter was added. + See :pep:`567` for more details. + + .. method:: remove_done_callback(callback) + + Remove *callback* from the callbacks list. + + Returns the number of callbacks removed, which is typically 1, + unless a callback was added more than once. + + .. method:: cancel() + + Cancel the Future and schedule callbacks. + + If the Future is already *done* or *cancelled*, return ``False``. + Otherwise, change the Future's state to *cancelled*, + schedule the callbacks, and return ``True``. + + .. method:: exception() + + Return the exception that was set on this Future. + + The exception (or ``None`` if no exception was set) is + returned only if the Future is *done*. + + If the Future has been *cancelled*, this method raises a + :exc:`CancelledError` exception. + + If the Future isn't *done* yet, this method raises an + :exc:`InvalidStateError` exception. + + .. method:: get_loop() + + Return the event loop the Future object is bound to. + + .. versionadded:: 3.7 + + .. method:: cancelled() + + Return ``True`` if the Future was *cancelled*. + + +This example creates a Future object, creates and schedules an +asynchronous Task to set result for the Future, and waits until +the Future has a result:: + + async def set_after(fut, delay, value): + # Sleep for *delay* seconds. + await asyncio.sleep(delay) + + # Set *value* as a result of *fut* Future. + fut.set_result(value) + + async def main(): + # Get the current event loop. + loop = asyncio.get_running_loop() + + # Create a new Future object. + fut = loop.create_future() + + # Run "set_after()" coroutine in a parallel Task. + # We are using the low-level "loop.create_task()" API here because + # we already have a reference to the event loop at hand. + # Otherwise we could have just used "asyncio.create_task()". + loop.create_task( + set_after(fut, 1, '... world')) + + print('hello ...') + + # Wait until *fut* has a result (1 second) and print it. + print(await fut) + + asyncio.run(main()) + + +.. important:: + + The Future object was designed to mimic + :class:`concurrent.futures.Future`. Key differences include: + + - unlike asyncio Futures, :class:`concurrent.futures.Future` + instances cannot be awaited. + + - :meth:`asyncio.Future.result` and :meth:`asyncio.Future.exception` + do not accept the *timeout* argument. + + - :meth:`asyncio.Future.result` and :meth:`asyncio.Future.exception` + raise an :exc:`InvalidStateError` exception when the Future is not + *done*. + + - Callbacks registered with :meth:`asyncio.Future.add_done_callback` + are not called immediately. They are scheduled with + :meth:`loop.call_soon` instead. + + - asyncio Future is not compatible with the + :func:`concurrent.futures.wait` and + :func:`concurrent.futures.as_completed` functions. diff --git a/Doc/library/asyncio-protocol.rst b/Doc/library/asyncio-protocol.rst index d7ecb25bcea..e4aed647c33 100644 --- a/Doc/library/asyncio-protocol.rst +++ b/Doc/library/asyncio-protocol.rst @@ -1,6 +1,9 @@ .. currentmodule:: asyncio +.. _asyncio-transports-protocols: + + ======================== Transports and Protocols ======================== @@ -393,11 +396,13 @@ Subprocess Transports .. method:: SubprocessTransport.kill() - Kill the subprocess, as in :meth:`subprocess.Popen.kill`. + Kill the subprocess. On POSIX systems, the function sends SIGKILL to the subprocess. On Windows, this method is an alias for :meth:`terminate`. + See also :meth:`subprocess.Popen.kill`. + .. method:: SubprocessTransport.send_signal(signal) Send the *signal* number to the subprocess, as in @@ -405,17 +410,20 @@ Subprocess Transports .. method:: SubprocessTransport.terminate() - Ask the subprocess to stop, as in :meth:`subprocess.Popen.terminate`. + Stop the subprocess. On POSIX systems, this method sends SIGTERM to the subprocess. On Windows, the Windows API function TerminateProcess() is called to stop the subprocess. + See also :meth:`subprocess.Popen.terminate`. + .. method:: SubprocessTransport.close() - Kill the subprocess by calling the :meth:`kill` method - if the subprocess hasn't returned yet, and close transports of all - pipes (*stdin*, *stdout* and *stderr*). + Kill the subprocess by calling the :meth:`kill` method. + + If the subprocess hasn't returned yet, and close transports of + *stdin*, *stdout*, and *stderr* pipes. .. _asyncio-protocol: diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 9f4433ddc33..7e09b166ebc 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -1,96 +1,118 @@ .. currentmodule:: asyncio -Tasks and coroutines + +==================== +Coroutines and Tasks ==================== -**Source code:** :source:`Lib/asyncio/tasks.py` +This section outlines high-level asyncio APIs to work with coroutines +and Tasks. + +.. contents:: + :depth: 1 + :local: -**Source code:** :source:`Lib/asyncio/coroutines.py` .. _coroutine: Coroutines ----------- +========== -Coroutines used with :mod:`asyncio` may be implemented using the -:keyword:`async def` statement, or by using :term:`generators `. -The :keyword:`async def` type of coroutine was added in Python 3.5, and -is recommended if there is no need to support older Python versions. +Coroutines declared with async/await syntax is the preferred way of +writing asyncio applications. For example, the following snippet +of code prints "hello", waits 1 second, and prints "world":: -Generator-based coroutines should be decorated with :func:`@asyncio.coroutine -`, although this is not strictly enforced. -The decorator enables compatibility with :keyword:`async def` coroutines, -and also serves as documentation. Generator-based -coroutines use the ``yield from`` syntax introduced in :pep:`380`, -instead of the original ``yield`` syntax. + >>> import asyncio -The word "coroutine", like the word "generator", is used for two -different (though related) concepts: + >>> async def main(): + ... print('hello') + ... await asyncio.sleep(1) + ... print('world') -- The function that defines a coroutine - (a function definition using :keyword:`async def` or - decorated with ``@asyncio.coroutine``). If disambiguation is needed - we will call this a *coroutine function* (:func:`iscoroutinefunction` - returns ``True``). + >>> asyncio.run(main()) + hello + world -- The object obtained by calling a coroutine function. This object - represents a computation or an I/O operation (usually a combination) - that will complete eventually. If disambiguation is needed we will - call it a *coroutine object* (:func:`iscoroutine` returns ``True``). +Note that simply calling a coroutine will not schedule it to +be executed:: -Things a coroutine can do: + >>> main() + -- ``result = await future`` or ``result = yield from future`` -- - suspends the coroutine until the - future is done, then returns the future's result, or raises an - exception, which will be propagated. (If the future is cancelled, - it will raise a ``CancelledError`` exception.) Note that tasks are - futures, and everything said about futures also applies to tasks. +To actually run a coroutine asyncio provides three main mechanisms: -- ``result = await coroutine`` or ``result = yield from coroutine`` -- - wait for another coroutine to - produce a result (or raise an exception, which will be propagated). - The ``coroutine`` expression must be a *call* to another coroutine. +* By using the :func:`asyncio.run` function to run the top-level + entry point "main()" function (see the above example.) -- ``return expression`` -- produce a result to the coroutine that is - waiting for this one using :keyword:`await` or ``yield from``. +* By awaiting on a coroutine. The following snippet of code will + print "hello" after waiting for 1 second, and then print "world" + after waiting for *another* 2 seconds:: -- ``raise exception`` -- raise an exception in the coroutine that is - waiting for this one using :keyword:`await` or ``yield from``. + import asyncio + import time -Calling a coroutine does not start its code running -- -the coroutine object returned by the call doesn't do anything until you -schedule its execution. There are two basic ways to start it running: -call ``await coroutine`` or ``yield from coroutine`` from another coroutine -(assuming the other coroutine is already running!), or schedule its execution -using the :func:`ensure_future` function or the :meth:`loop.create_task` -method. + async def say_after(delay, what): + await asyncio.sleep(delay) + print(what) + + async def main(): + print('started at', time.strftime('%X')) + + await say_after(1, 'hello') + await say_after(2, 'world') + + print('finished at', time.strftime('%X')) + + asyncio.run(main()) + + Expected output:: + + started at 17:13:52 + hello + world + finished at 17:13:55 + +* By using the :func:`asyncio.create_task` function to run coroutines + concurrently as asyncio :class:`Tasks `. + + Let's modify the above example and run two "set_after" coroutines + *concurrently*:: + + async def main(): + task1 = asyncio.create_task( + say_after(1, 'hello')) + + task2 = asyncio.create_task( + say_after(2, 'world')) + + print('started at', time.strftime('%X')) + + # Wait until both tasks are completed (should take + # around 2 seconds.) + await task1 + await task2 + + print('finished at', time.strftime('%X')) + + Note that expected output now shows that the snippet runs + 1 second faster than before:: + + started at 17:14:32 + hello + world + finished at 17:14:34 + +Note that in this documentation the term "coroutine" can be used for +two closely related concepts: + +* a *coroutine function*: an :keyword:`async def` function; + +* a *coroutine object*: object returned by calling a + *coroutine function*. -Coroutines (and tasks) can only run when the event loop is running. - -.. decorator:: coroutine - - Decorator to mark generator-based coroutines. This enables - the generator use :keyword:`!yield from` to call :keyword:`async - def` coroutines, and also enables the generator to be called by - :keyword:`async def` coroutines, for instance using an - :keyword:`await` expression. - - There is no need to decorate :keyword:`async def` coroutines themselves. - - If the generator is not yielded from before it is destroyed, an error - message is logged. See :ref:`Detect coroutines never scheduled - `. - -.. note:: - - In this documentation, some methods are documented as coroutines, - even if they are plain Python functions returning a :class:`Future`. - This is intentional to have a freedom of tweaking the implementation - of these functions in the future. If such a function is needed to be - used in a callback-style code, wrap its result with :func:`ensure_future`. - +Running an asyncio Program +========================== .. function:: run(coro, \*, debug=False) @@ -101,7 +123,7 @@ Coroutines (and tasks) can only run when the event loop is running. This function cannot be called when another asyncio event loop is running in the same thread. - If debug is True, the event loop will be run in debug mode. + If *debug* is ``True``, the event loop will be run in debug mode. This function always creates a new event loop and closes it at the end. It should be used as a main entry point for asyncio @@ -112,260 +134,8 @@ Coroutines (and tasks) can only run when the event loop is running. on a :term:`provisional basis `. -.. _asyncio-hello-world-coroutine: - -Example: Hello World coroutine -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Example of coroutine displaying ``"Hello World"``:: - - import asyncio - - async def hello_world(): - print("Hello World!") - - asyncio.run(hello_world()) - -.. seealso:: - - The :ref:`Hello World with call_soon() ` - example uses the :meth:`loop.call_soon` method to schedule a - callback. - - -.. _asyncio-date-coroutine: - -Example: Coroutine displaying the current date -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Example of coroutine displaying the current date every second during 5 seconds -using the :meth:`sleep` function:: - - import asyncio - import datetime - - async def display_date(): - loop = asyncio.get_running_loop() - end_time = loop.time() + 5.0 - while True: - print(datetime.datetime.now()) - if (loop.time() + 1.0) >= end_time: - break - await asyncio.sleep(1) - - asyncio.run(display_date()) - -.. seealso:: - - The :ref:`display the current date with call_later() - ` example uses a callback with the - :meth:`loop.call_later` method. - - -Example: Chain coroutines -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Example chaining coroutines:: - - import asyncio - - async def compute(x, y): - print("Compute %s + %s ..." % (x, y)) - await asyncio.sleep(1.0) - return x + y - - async def print_sum(x, y): - result = await compute(x, y) - print("%s + %s = %s" % (x, y, result)) - - loop = asyncio.get_event_loop() - loop.run_until_complete(print_sum(1, 2)) - loop.close() - -``compute()`` is chained to ``print_sum()``: ``print_sum()`` coroutine waits -until ``compute()`` is completed before returning its result. - -Sequence diagram of the example: - -.. image:: tulip_coro.png - :align: center - -The "Task" is created by the :meth:`loop.run_until_complete` method -when it gets a coroutine object instead of a task. - -The diagram shows the control flow, it does not describe exactly how things -work internally. For example, the sleep coroutine creates an internal future -which uses :meth:`loop.call_later` to wake up the task in 1 second. - - -Future ------- - -.. class:: Future(\*, loop=None) - - This class is *almost* compatible with :class:`concurrent.futures.Future`. - - Differences: - - - :meth:`result` and :meth:`exception` do not take a timeout argument and - raise an exception when the future isn't done yet. - - - Callbacks registered with :meth:`add_done_callback` are always called - via the event loop's :meth:`loop.call_soon`. - - - This class is not compatible with the :func:`~concurrent.futures.wait` and - :func:`~concurrent.futures.as_completed` functions in the - :mod:`concurrent.futures` package. - - This class is :ref:`not thread safe `. - - .. method:: cancel() - - Cancel the future and schedule callbacks. - - If the future is already done or cancelled, return ``False``. Otherwise, - change the future's state to cancelled, schedule the callbacks and return - ``True``. - - .. method:: cancelled() - - Return ``True`` if the future was cancelled. - - .. method:: done() - - Return ``True`` if the future is done. - - Done means either that a result / exception are available, or that the - future was cancelled. - - .. method:: result() - - Return the result this future represents. - - If the future has been cancelled, raises :exc:`CancelledError`. If the - future's result isn't yet available, raises :exc:`InvalidStateError`. If - the future is done and has an exception set, this exception is raised. - - .. method:: exception() - - Return the exception that was set on this future. - - The exception (or ``None`` if no exception was set) is returned only if - the future is done. If the future has been cancelled, raises - :exc:`CancelledError`. If the future isn't done yet, raises - :exc:`InvalidStateError`. - - .. method:: add_done_callback(callback, *, context=None) - - Add a callback to be run when the future becomes done. - - The *callback* is called with a single argument - the future object. If the - future is already done when this is called, the callback is scheduled - with :meth:`loop.call_soon`. - - An optional keyword-only *context* argument allows specifying a custom - :class:`contextvars.Context` for the *callback* to run in. The current - context is used when no *context* is provided. - - :ref:`Use functools.partial to pass parameters to the callback - `. For example, - ``fut.add_done_callback(functools.partial(print, "Future:", - flush=True))`` will call ``print("Future:", fut, flush=True)``. - - .. versionchanged:: 3.7 - The *context* keyword-only parameter was added. See :pep:`567` - for more details. - - .. method:: remove_done_callback(fn) - - Remove all instances of a callback from the "call when done" list. - - Returns the number of callbacks removed. - - .. method:: set_result(result) - - Mark the future done and set its result. - - If the future is already done when this method is called, raises - :exc:`InvalidStateError`. - - .. method:: set_exception(exception) - - Mark the future done and set an exception. - - If the future is already done when this method is called, raises - :exc:`InvalidStateError`. - - .. method:: get_loop() - - Return the event loop the future object is bound to. - - .. versionadded:: 3.7 - - -Example: Future with run_until_complete() -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Example combining a :class:`Future` and a :ref:`coroutine function -`:: - - import asyncio - - async def slow_operation(future): - await asyncio.sleep(1) - future.set_result('Future is done!') - - loop = asyncio.get_event_loop() - future = asyncio.Future() - asyncio.ensure_future(slow_operation(future)) - loop.run_until_complete(future) - print(future.result()) - loop.close() - -The coroutine function is responsible for the computation (which takes 1 second) -and it stores the result into the future. The -:meth:`loop.run_until_complete` method waits for the completion of -the future. - -.. note:: - The :meth:`loop.run_until_complete` method uses internally the - :meth:`~Future.add_done_callback` method to be notified when the future is - done. - - -Example: Future with run_forever() -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The previous example can be written differently using the -:meth:`Future.add_done_callback` method to describe explicitly the control -flow:: - - import asyncio - - async def slow_operation(future): - await asyncio.sleep(1) - future.set_result('Future is done!') - - def got_result(future): - print(future.result()) - loop.stop() - - loop = asyncio.get_event_loop() - future = asyncio.Future() - asyncio.ensure_future(slow_operation(future)) - future.add_done_callback(got_result) - try: - loop.run_forever() - finally: - loop.close() - -In this example, the future is used to link ``slow_operation()`` to -``got_result()``: when ``slow_operation()`` is done, ``got_result()`` is called -with the result. - - -Task ----- +Creating Tasks +============== .. function:: create_task(coro, \*, name=None) @@ -384,359 +154,136 @@ Task .. versionchanged:: 3.8 Added the ``name`` parameter. -.. class:: Task(coro, \*, loop=None, name=None) - A unit for concurrent running of :ref:`coroutines `, - subclass of :class:`Future`. - - A task is responsible for executing a coroutine object in an event loop. If - the wrapped coroutine yields from a future, the task suspends the execution - of the wrapped coroutine and waits for the completion of the future. When - the future is done, the execution of the wrapped coroutine restarts with the - result or the exception of the future. - - Event loops use cooperative scheduling: an event loop only runs one task at - a time. Other tasks may run in parallel if other event loops are - running in different threads. While a task waits for the completion of a - future, the event loop executes a new task. - - The cancellation of a task is different from the cancellation of a - future. Calling :meth:`cancel` will throw a - :exc:`~concurrent.futures.CancelledError` to the wrapped - coroutine. :meth:`~Future.cancelled` only returns ``True`` if the - wrapped coroutine did not catch the - :exc:`~concurrent.futures.CancelledError` exception, or raised a - :exc:`~concurrent.futures.CancelledError` exception. - - If a pending task is destroyed, the execution of its wrapped :ref:`coroutine - ` did not complete. It is probably a bug and a warning is - logged: see :ref:`Pending task destroyed `. - - Don't directly create :class:`Task` instances: use the :func:`create_task` - function or the :meth:`loop.create_task` method. - - Tasks support the :mod:`contextvars` module. When a Task - is created it copies the current context and later runs its coroutine - in the copied context. See :pep:`567` for more details. - - This class is :ref:`not thread safe `. - - .. versionchanged:: 3.7 - Added support for the :mod:`contextvars` module. - - .. versionchanged:: 3.8 - Added the ``name`` parameter. - - .. classmethod:: all_tasks(loop=None) - - Return a set of all tasks for an event loop. - - By default all tasks for the current event loop are returned. - If *loop* is ``None``, :func:`get_event_loop` function - is used to get the current loop. - - .. classmethod:: current_task(loop=None) - - Return the currently running task in an event loop or ``None``. - - By default the current task for the current event loop is returned. - - ``None`` is returned when called not in the context of a :class:`Task`. - - .. method:: cancel() - - Request that this task cancel itself. - - This arranges for a :exc:`~concurrent.futures.CancelledError` to be - thrown into the wrapped coroutine on the next cycle through the event - loop. The coroutine then has a chance to clean up or even deny the - request using try/except/finally. - - Unlike :meth:`Future.cancel`, this does not guarantee that the task - will be cancelled: the exception might be caught and acted upon, delaying - cancellation of the task or preventing cancellation completely. The task - may also return a value or raise a different exception. - - Immediately after this method is called, :meth:`~Future.cancelled` will - not return ``True`` (unless the task was already cancelled). A task will - be marked as cancelled when the wrapped coroutine terminates with a - :exc:`~concurrent.futures.CancelledError` exception (even if - :meth:`cancel` was not called). - - .. method:: get_stack(\*, limit=None) - - Return the list of stack frames for this task's coroutine. - - If the coroutine is not done, this returns the stack where it is - suspended. If the coroutine has completed successfully or was - cancelled, this returns an empty list. If the coroutine was - terminated by an exception, this returns the list of traceback - frames. - - The frames are always ordered from oldest to newest. - - The optional limit gives the maximum number of frames to return; by - default all available frames are returned. Its meaning differs depending - on whether a stack or a traceback is returned: the newest frames of a - stack are returned, but the oldest frames of a traceback are returned. - (This matches the behavior of the traceback module.) - - For reasons beyond our control, only one stack frame is returned for a - suspended coroutine. - - .. method:: print_stack(\*, limit=None, file=None) - - Print the stack or traceback for this task's coroutine. - - This produces output similar to that of the traceback module, for the - frames retrieved by get_stack(). The limit argument is passed to - get_stack(). The file argument is an I/O stream to which the output - is written; by default output is written to sys.stderr. - - .. method:: get_name() - - Return the name of the task. - - If no name has been explicitly assigned to the task, the default - ``Task`` implementation generates a default name during instantiation. - - .. versionadded:: 3.8 - - .. method:: set_name(value) - - Set the name of the task. - - The *value* argument can be any object, which is then converted to a - string. - - In the default ``Task`` implementation, the name will be visible in the - :func:`repr` output of a task object. - - .. versionadded:: 3.8 - - -Example: Parallel execution of tasks -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Example executing 3 tasks (A, B, C) in parallel:: - - import asyncio - - async def factorial(name, number): - f = 1 - for i in range(2, number+1): - print("Task %s: Compute factorial(%s)..." % (name, i)) - await asyncio.sleep(1) - f *= i - print("Task %s: factorial(%s) = %s" % (name, number, f)) - - loop = asyncio.get_event_loop() - loop.run_until_complete(asyncio.gather( - factorial("A", 2), - factorial("B", 3), - factorial("C", 4), - )) - loop.close() - -Output:: - - Task A: Compute factorial(2)... - Task B: Compute factorial(2)... - Task C: Compute factorial(2)... - Task A: factorial(2) = 2 - Task B: Compute factorial(3)... - Task C: Compute factorial(3)... - Task B: factorial(3) = 6 - Task C: Compute factorial(4)... - Task C: factorial(4) = 24 - -A task is automatically scheduled for execution when it is created. The event -loop stops when all tasks are done. - - -Task functions --------------- - -.. note:: - - In the functions below, the optional *loop* argument allows explicitly setting - the event loop object used by the underlying task or coroutine. If it's - not provided, the default event loop is used. - - -.. function:: current_task(loop=None) - - Return the current running :class:`Task` instance or ``None``, if - no task is running. - - If *loop* is ``None`` :func:`get_running_loop` is used to get - the current loop. - - .. versionadded:: 3.7 - - -.. function:: all_tasks(loop=None) - - Return a set of :class:`Task` objects created for the loop. - - If *loop* is ``None``, :func:`get_running_loop` is used for getting - current loop (contrary to the deprecated :meth:`Task.all_tasks` method - that uses :func:`get_event_loop`.) - - .. versionadded:: 3.7 - - -.. function:: as_completed(fs, \*, loop=None, timeout=None) - - Return an iterator whose values, when waited for, are :class:`Future` - instances. - - Raises :exc:`asyncio.TimeoutError` if the timeout occurs before all Futures - are done. - - Example:: - - for f in as_completed(fs): - result = await f # The 'await' may raise - # Use result - - .. note:: - - The futures ``f`` are not necessarily members of fs. - -.. function:: ensure_future(coro_or_future, \*, loop=None) - - Schedule the execution of a :ref:`coroutine object `: wrap it in - a future. Return a :class:`Task` object. - - If the argument is a :class:`Future`, it is returned directly. - - .. versionadded:: 3.4.4 - - .. versionchanged:: 3.5.1 - The function accepts any :term:`awaitable` object. - - .. note:: - - :func:`create_task` (added in Python 3.7) is the preferable way - for spawning new tasks. - - .. seealso:: - - The :func:`create_task` function and - :meth:`loop.create_task` method. - -.. function:: wrap_future(future, \*, loop=None) - - Wrap a :class:`concurrent.futures.Future` object in a :class:`Future` - object. - -.. function:: gather(\*coros_or_futures, loop=None, return_exceptions=False) - - Return a future aggregating results from the given coroutine objects or - futures. - - All futures must share the same event loop. If all the tasks are done - successfully, the returned future's result is the list of results (in the - order of the original sequence, not necessarily the order of results - arrival). If *return_exceptions* is true, exceptions in the tasks are - treated the same as successful results, and gathered in the result list; - otherwise, the first raised exception will be immediately propagated to the - returned future. - - Cancellation: if the outer Future is cancelled, all children (that have not - completed yet) are also cancelled. If any child is cancelled, this is - treated as if it raised :exc:`~concurrent.futures.CancelledError` -- the - outer Future is *not* cancelled in this case. (This is to prevent the - cancellation of one child to cause other children to be cancelled.) - - .. versionchanged:: 3.7.0 - If the *gather* itself is cancelled, the cancellation is propagated - regardless of *return_exceptions*. - -.. function:: iscoroutine(obj) - - Return ``True`` if *obj* is a :ref:`coroutine object `, - which may be based on a generator or an :keyword:`async def` coroutine. - -.. function:: iscoroutinefunction(func) - - Return ``True`` if *func* is determined to be a :ref:`coroutine function - `, which may be a decorated generator function or an - :keyword:`async def` function. - -.. function:: run_coroutine_threadsafe(coro, loop) - - Submit a :ref:`coroutine object ` to a given event loop. - - Return a :class:`concurrent.futures.Future` to access the result. - - This function is meant to be called from a different thread than the one - where the event loop is running. Usage:: - - # Create a coroutine - coro = asyncio.sleep(1, result=3) - # Submit the coroutine to a given loop - future = asyncio.run_coroutine_threadsafe(coro, loop) - # Wait for the result with an optional timeout argument - assert future.result(timeout) == 3 - - If an exception is raised in the coroutine, the returned future will be - notified. It can also be used to cancel the task in the event loop:: - - try: - result = future.result(timeout) - except asyncio.TimeoutError: - print('The coroutine took too long, cancelling the task...') - future.cancel() - except Exception as exc: - print('The coroutine raised an exception: {!r}'.format(exc)) - else: - print('The coroutine returned: {!r}'.format(result)) - - See the :ref:`concurrency and multithreading ` - section of the documentation. - - .. note:: - - Unlike other functions from the module, - :func:`run_coroutine_threadsafe` requires the *loop* argument to - be passed explicitly. - - .. versionadded:: 3.5.1 +Sleeping +======== .. coroutinefunction:: sleep(delay, result=None, \*, loop=None) - Create a :ref:`coroutine ` that completes after a given - time (in seconds). If *result* is provided, it is produced to the caller + Block for *delay* seconds. + + If *result* is provided, it is returned to the caller when the coroutine completes. - The resolution of the sleep depends on the :ref:`granularity of the event - loop `. + .. _asyncio-date-coroutine: - This function is a :ref:`coroutine `. + Example of coroutine displaying the current date every second + during 5 seconds:: -.. coroutinefunction:: shield(arg, \*, loop=None) + import asyncio + import datetime - Wait for a future, shielding it from cancellation. + async def display_date(): + loop = asyncio.get_running_loop() + end_time = loop.time() + 5.0 + while True: + print(datetime.datetime.now()) + if (loop.time() + 1.0) >= end_time: + break + await asyncio.sleep(1) + + asyncio.run(display_date()) + + +Running Tasks Concurrently +========================== + +.. coroutinefunction:: gather(\*fs, loop=None, return_exceptions=False) + + Return a Future aggregating results from the given coroutine objects, + Tasks, or Futures. + + If all Tasks/Futures are completed successfully, the result is an + aggregate list of returned values. The result values are in the + order of the original *fs* sequence. + + All coroutines in the *fs* list are automatically + wrapped in :class:`Tasks `. + + If *return_exceptions* is ``True``, exceptions in the Tasks/Futures + are treated the same as successful results, and gathered in the + result list. Otherwise, the first raised exception is immediately + propagated to the returned Future. + + If the outer Future is *cancelled*, all submitted Tasks/Futures + (that have not completed yet) are also *cancelled*. + + If any child is *cancelled*, it is treated as if it raised + :exc:`CancelledError` -- the outer Future is **not** cancelled in + this case. This is to prevent the cancellation of one submitted + Task/Future to cause other Tasks/Futures to be cancelled. + + All futures must share the same event loop. + + .. versionchanged:: 3.7 + If the *gather* itself is cancelled, the cancellation is + propagated regardless of *return_exceptions*. + + Example:: + + import asyncio + + async def factorial(name, number): + f = 1 + for i in range(2, number + 1): + print(f"Task {name}: Compute factorial({i})...") + await asyncio.sleep(1) + f *= i + print(f"Task {name}: factorial({number}) = {f}") + + async def main(): + await asyncio.gather( + factorial("A", 2), + factorial("B", 3), + factorial("C", 4), + )) + + asyncio.run(main()) + + # Expected output: + # + # Task A: Compute factorial(2)... + # Task B: Compute factorial(2)... + # Task C: Compute factorial(2)... + # Task A: factorial(2) = 2 + # Task B: Compute factorial(3)... + # Task C: Compute factorial(3)... + # Task B: factorial(3) = 6 + # Task C: Compute factorial(4)... + # Task C: factorial(4) = 24 + + +Shielding Tasks From Cancellation +================================= + +.. coroutinefunction:: shield(fut, \*, loop=None) + + Wait for a Future/Task while protecting it from being cancelled. + + *fut* can be a coroutine, a Task, or a Future-like object. If + *fut* is a coroutine it is automatically wrapped in a + :class:`Task`. The statement:: res = await shield(something()) - is exactly equivalent to the statement:: + is equivalent to the statement:: res = await something() - *except* that if the coroutine containing it is cancelled, the task running - in ``something()`` is not cancelled. From the point of view of - ``something()``, the cancellation did not happen. But its caller is still - cancelled, so the yield-from expression still raises - :exc:`~concurrent.futures.CancelledError`. Note: If ``something()`` is - cancelled by other means this will still cancel ``shield()``. + *except* that if the coroutine containing it is cancelled, the + Task running in ``something()`` is not cancelled. From the point + of view of ``something()``, the cancellation did not happen. + Although its caller is still cancelled, so the "await" expression + still raises :exc:`CancelledError`. - If you want to completely ignore cancellation (not recommended) you can - combine ``shield()`` with a try/except clause, as follows:: + If ``something()`` is cancelled by other means (i.e. from within + itself) that would also cancel ``shield()``. + + If it is desired to completely ignore cancellation (not recommended) + the ``shield()`` function should be combined with a try/except + clause, as follows:: try: res = await shield(something()) @@ -744,21 +291,79 @@ Task functions res = None -.. coroutinefunction:: wait(futures, \*, loop=None, timeout=None,\ +Timeouts +======== + +.. coroutinefunction:: wait_for(fut, timeout, \*, loop=None) + + Wait for the coroutine, Task, or Future to complete with timeout. + + *fut* can be a coroutine, a Task, or a Future-like object. If + *fut* is a coroutine it is automatically wrapped in a + :class:`Task`. + + *timeout* can either be ``None`` or a float or int number of seconds + to wait for. If *timeout* is ``None``, block until the future + completes. + + If a timeout occurs, it cancels the task and raises + :exc:`asyncio.TimeoutError`. + + To avoid the task cancellation, wrap it in :func:`shield`. + + The function will wait until the future is actually cancelled, + so the total wait time may exceed the *timeout*. + + If the wait is cancelled, the future *fut* is also cancelled. + + Example:: + + async def eternity(): + # Sleep for one hour + await asyncio.sleep(3600) + print('yay!') + + async def main(): + # Wait for at most 1 second + try: + await asyncio.wait_for(eternity(), timeout=1.0) + except asyncio.TimeoutError: + print('timeout!') + + asyncio.run(main()) + + # Expected output: + # + # timeout! + + .. versionchanged:: 3.7 + When *fut* is cancelled due to a timeout, ``wait_for`` waits + for *fut* to be cancelled. Previously, it raised + :exc:`asyncio.TimeoutError` immediately. + + +Waiting Primitives +================== + +.. coroutinefunction:: wait(fs, \*, loop=None, timeout=None,\ return_when=ALL_COMPLETED) - Wait for the Futures and coroutine objects given by the sequence *futures* - to complete. Coroutines will be wrapped in Tasks. Returns two sets of - :class:`Future`: (done, pending). + Wait for a set of Futures to complete. - The sequence *futures* must not be empty. + *fs* is a list of coroutines, Futures, and/or Tasks. Coroutines + are automatically wrapped in :class:`Tasks `. - *timeout* can be used to control the maximum number of seconds to wait before - returning. *timeout* can be an int or float. If *timeout* is not specified - or ``None``, there is no limit to the wait time. + Returns two sets of Tasks/Futures: ``(done, pending)``. - *return_when* indicates when this function should return. It must be one of - the following constants of the :mod:`concurrent.futures` module: + *timeout* (a float or int), if specified, can be used to control + the maximum number of seconds to wait before returning. + + Note that this function does not raise :exc:`asyncio.TimeoutError`. + Futures or Tasks that aren't done when the timeout occurs are just + returned in the second set. + + *return_when* indicates when this function should return. It must + be one of the following constants: .. tabularcolumns:: |l|L| @@ -778,45 +383,343 @@ Task functions | | futures finish or are cancelled. | +-----------------------------+----------------------------------------+ - Unlike :func:`~asyncio.wait_for`, ``wait()`` will not cancel the futures - when a timeout occurs. - - This function is a :ref:`coroutine `. + Unlike :func:`~asyncio.wait_for`, ``wait()`` does not cancel the + futures when a timeout occurs. Usage:: done, pending = await asyncio.wait(fs) - .. note:: - This does not raise :exc:`asyncio.TimeoutError`! Futures that aren't done - when the timeout occurs are returned in the second set. +.. function:: as_completed(fs, \*, loop=None, timeout=None) + + Return an iterator which values, when waited for, are + :class:`Future` instances. + + Raises :exc:`asyncio.TimeoutError` if the timeout occurs before + all Futures are done. + + Example:: + + for f in as_completed(fs): + result = await f + # ... -.. coroutinefunction:: wait_for(fut, timeout, \*, loop=None) +Scheduling From Other Threads +============================= - Wait for the single :class:`Future` or :ref:`coroutine object ` - to complete with timeout. If *timeout* is ``None``, block until the future - completes. +.. function:: run_coroutine_threadsafe(coro, loop) - Coroutine will be wrapped in :class:`Task`. + Submit a coroutine to the given event loop. Thread-safe. - Returns result of the Future or coroutine. When a timeout occurs, it - cancels the task and raises :exc:`asyncio.TimeoutError`. To avoid the task - cancellation, wrap it in :func:`shield`. The function will wait until - the future is actually cancelled, so the total wait time may exceed - the *timeout*. + Return a :class:`concurrent.futures.Future` to access the result. - If the wait is cancelled, the future *fut* is also cancelled. + This function is meant to be called from a different OS thread + than the one where the event loop is running. Example:: - This function is a :ref:`coroutine `, usage:: + # Create a coroutine + coro = asyncio.sleep(1, result=3) - result = await asyncio.wait_for(fut, 60.0) + # Submit the coroutine to a given loop + future = asyncio.run_coroutine_threadsafe(coro, loop) - .. versionchanged:: 3.4.3 - If the wait is cancelled, the future *fut* is now also cancelled. + # Wait for the result with an optional timeout argument + assert future.result(timeout) == 3 + + If an exception is raised in the coroutine, the returned Future + will be notified. It can also be used to cancel the task in + the event loop:: + + try: + result = future.result(timeout) + except asyncio.TimeoutError: + print('The coroutine took too long, cancelling the task...') + future.cancel() + except Exception as exc: + print('The coroutine raised an exception: {!r}'.format(exc)) + else: + print('The coroutine returned: {!r}'.format(result)) + + See the :ref:`concurrency and multithreading ` + section of the documentation. + + Unlike other asyncio functions this functions requires the *loop* + argument to be passed explicitly. + + .. versionadded:: 3.5.1 + + +Introspection +============= + + +.. function:: current_task(loop=None) + + Return the currently running :class:`Task` instance, or ``None`` if + no task is running. + + If *loop* is ``None`` :func:`get_running_loop` is used to get + the current loop. + + .. versionadded:: 3.7 + + +.. function:: all_tasks(loop=None) + + Return a set of not yet finished :class:`Task` objects run by + the loop. + + If *loop* is ``None``, :func:`get_running_loop` is used for getting + current loop. + + .. versionadded:: 3.7 + + +Task Object +=========== + +.. class:: Task(coro, \*, loop=None, name=None) + + A :class:`Future`-like object that wraps a Python + :ref:`coroutine `. Not thread-safe. + + Tasks are used to run coroutines in event loops. + If a coroutine awaits on a Future, the Task suspends + the execution of the coroutine and waits for the completion + of the Future. When the Future is *done*, the execution of + the wrapped coroutine resumes. + + Event loops use cooperative scheduling: an event loop runs + one Task at a time. While a Task awaits for the completion of a + Future, the event loop runs other Tasks, callbacks, or performs + IO operations. + + Use the high-level :func:`asyncio.create_task` function to create + Tasks, or low-level :meth:`loop.create_task` or + :func:`ensure_future` functions. Manually instantiating Task + objects is discouraged. + + To cancel a running Task use the :meth:`cancel` method. Calling it + will cause the Task to throw a :exc:`CancelledError` exception into + the wrapped coroutine. If a coroutine is awaiting on a Future + object during cancellation, the Future object will be cancelled. + + :meth:`cancelled` can be used to check if the Task was cancelled. + The method returns ``True`` if the wrapped coroutine did not + suppress the :exc:`CancelledError` exception and was actually + cancelled. + + :class:`asyncio.Task` inherits from :class:`Future` all of its + APIs except :meth:`Future.set_result` and + :meth:`Future.set_exception`. + + Tasks support the :mod:`contextvars` module. When a Task + is created it copies the current context and later runs its + coroutine in the copied context. .. versionchanged:: 3.7 - When *fut* is cancelled due to a timeout, ``wait_for`` now waits - for *fut* to be cancelled. Previously, - it raised :exc:`~asyncio.TimeoutError` immediately. + Added support for the :mod:`contextvars` module. + + .. versionchanged:: 3.8 + Added the ``name`` parameter. + + .. method:: cancel() + + Request the Task to be cancelled. + + This arranges for a :exc:`CancelledError` exception to be thrown + into the wrapped coroutine on the next cycle of the event loop. + + The coroutine then has a chance to clean up or even deny the + request by suppressing the exception with a :keyword:`try` ... + ... ``except CancelledError`` ... :keyword:`finally` block. + Therefore, unlike :meth:`Future.cancel`, :meth:`Task.cancel` does + not guarantee that the Task will be cancelled, although + suppressing cancellation completely is not common and is actively + discouraged. + + The following example illustrates how coroutines can intercept + the cancellation request:: + + async def cancel_me(): + print('cancel_me(): before sleep') + + try: + # Wait for 1 hour + await asyncio.sleep(3600) + except asyncio.CancelledError: + print('cancel_me(): cancel sleep') + raise + finally: + print('cancel_me(): after sleep') + + async def main(): + # Create a "cancel_me" Task + task = asyncio.create_task(cancel_me()) + + # Wait for 1 second + await asyncio.sleep(1) + + task.cancel() + try: + await task + except asyncio.CancelledError: + print("main(): cancel_me is cancelled now") + + asyncio.run(main()) + + # Expected output: + # + # cancel_me(): before sleep + # cancel_me(): cancel sleep + # cancel_me(): after sleep + # main(): cancel_me is cancelled now + + .. method:: cancelled() + + Return ``True`` if the Task is *cancelled*. + + The Task is *cancelled* when the cancellation was requested with + :meth:`cancel` and the wrapped coroutine propagated the + :exc:`CancelledError` exception thrown into it. + + .. method:: done() + + Return ``True`` if the Task is *done*. + + A Task is *done* when the wrapped coroutine either returned + a value, raised an exception, or the Task was cancelled. + + .. method:: get_stack(\*, limit=None) + + Return the list of stack frames for this Task. + + If the wrapped coroutine is not done, this returns the stack + where it is suspended. If the coroutine has completed + successfully or was cancelled, this returns an empty list. + If the coroutine was terminated by an exception, this returns + the list of traceback frames. + + The frames are always ordered from oldest to newest. + + Only one stack frame is returned for a suspended coroutine. + + The optional *limit* argument sets the maximum number of frames + to return; by default all available frames are returned. + The ordering of the returned list differs depending on whether + a stack or a traceback is returned: the newest frames of a + stack are returned, but the oldest frames of a traceback are + returned. (This matches the behavior of the traceback module.) + + .. method:: print_stack(\*, limit=None, file=None) + + Print the stack or traceback for this Task. + + This produces output similar to that of the traceback module + for the frames retrieved by :meth:`get_stack`. + + The *limit* argument is passed to :meth:`get_stack` directly. + + The *file* argument is an I/O stream to which the output + is written; by default output is written to :data:`sys.stderr`. + + .. method:: get_name() + + Return the name of the Task. + + If no name has been explicitly assigned to the Task, the default + asyncio Task implementation generates a default name during + instantiation. + + .. versionadded:: 3.8 + + .. method:: set_name(value) + + Set the name of the Task. + + The *value* argument can be any object, which is then + converted to a string. + + In the default Task implementation, the name will be visible + in the :func:`repr` output of a task object. + + .. versionadded:: 3.8 + + .. classmethod:: all_tasks(loop=None) + + Return a set of all tasks for an event loop. + + By default all tasks for the current event loop are returned. + If *loop* is ``None``, the :func:`get_event_loop` function + is used to get the current loop. + + This function is **deprecated** and scheduled for removal in + Python 3.9. Use the :func:`all_tasks` function instead. + + .. classmethod:: current_task(loop=None) + + Return the currently running task or ``None``. + + If *loop* is ``None``, the :func:`get_event_loop` function + is used to get the current loop. + + This function is **deprecated** and scheduled for removal in + Python 3.9. Use the :func:`current_task` function instead. + + +.. _asyncio_generator_based_coro: + +Generator-based Coroutines +========================== + +.. note:: + + Support for generator-based coroutines is **deprecated** and + scheduled for removal in Python 4.0. + +Generator-based coroutines predate async/await syntax. They are +Python generators that use ``yield from`` expression is to await +on Futures and other coroutines. + +Generator-based coroutines should be decorated with +:func:`@asyncio.coroutine `, although this is not +enforced. + + +.. decorator:: coroutine + + Decorator to mark generator-based coroutines. + + This decorator enables legacy generator-based coroutines to be + compatible with async/await code:: + + @asyncio.coroutine + def old_style_coroutine(): + yield from asyncio.sleep(1) + + async def main(): + await old_style_coroutine() + + This decorator is **deprecated** and is scheduled for removal in + Python 4.0. + + This decorator should not be used for :keyword:`async def` + coroutines. + +.. function:: iscoroutine(obj) + + Return ``True`` if *obj* is a :ref:`coroutine object `. + + This method is different from :func:`inspect.iscoroutine` because + it returns ``True`` for generator-based coroutines decorated with + :func:`@coroutine `. + +.. function:: iscoroutinefunction(func) + + Return ``True`` if *func* is a :ref:`coroutine function + `. + + This method is different from :func:`inspect.iscoroutinefunction` + because it returns ``True`` for generator-based coroutine functions + decorated with :func:`@coroutine `. diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index 0d58a94c8b9..2c8ccbb390f 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -1,15 +1,13 @@ -:mod:`asyncio` --- Asynchronous I/O, event loop, coroutines and tasks -===================================================================== +:mod:`asyncio` --- Asynchronous I/O +=================================== .. module:: asyncio - :synopsis: Asynchronous I/O, event loop, coroutines and tasks. - -.. versionadded:: 3.4 - -**Source code:** :source:`Lib/asyncio/` + :synopsis: Asynchronous I/O. -------------- +.. TODO: rewrite the introduction section + This module provides infrastructure for writing single-threaded concurrent code using coroutines, multiplexing I/O access over sockets and other resources, running network clients and servers, and other related primitives. @@ -44,25 +42,40 @@ programming: see the :ref:`Develop with asyncio ` page which lists common traps and explains how to avoid them. :ref:`Enable the debug mode ` during development to detect common issues. -Table of contents: +High-level APIs: .. toctree:: - :maxdepth: 3 + :maxdepth: 1 + + asyncio-task.rst + asyncio-stream.rst + asyncio-sync.rst + asyncio-subprocess.rst + asyncio-queue.rst + asyncio-exceptions.rst + +Low-level APIs: + +.. toctree:: + :maxdepth: 1 asyncio-eventloop.rst + asyncio-future.rst + asyncio-protocol.rst asyncio-policy.rst asyncio-platforms.rst - asyncio-task.rst - asyncio-protocol.rst - asyncio-stream.rst - asyncio-subprocess.rst - asyncio-sync.rst - asyncio-queue.rst + +Guides and Tutorials: + +.. toctree:: + :maxdepth: 1 + asyncio-dev.rst - asyncio-exceptions.rst + .. seealso:: - The :mod:`asyncio` module was designed in :PEP:`3156`. For a - motivational primer on transports and protocols, see :PEP:`3153`. - + The :mod:`asyncio` module was proposed in :PEP:`3156`. + Since the acceptance of the PEP many new APIs were added and many + original APIs were altered. The PEP should be treated as a + historical document.