Update for asyncio PEP (#105)
This commit is contained in:
parent
4f899a0f01
commit
317cede6ec
125
pep-3156.txt
125
pep-3156.txt
|
@ -116,25 +116,26 @@ environment forces the issue. (An example would be a platform where
|
|||
there is a system event loop that cannot be started or stopped; see
|
||||
"Embedded Event Loops" below.)
|
||||
|
||||
The event loop API does not depend on ``yield from``. Rather, it uses
|
||||
The event loop API does not depend on ``await/yield from``. Rather, it uses
|
||||
a combination of callbacks, additional interfaces (transports and
|
||||
protocols), and Futures. The latter are similar to those defined in
|
||||
PEP 3148, but have a different implementation and are not tied to
|
||||
threads. In particular, the ``result()`` method raises an exception
|
||||
instead of blocking when a result is not yet ready; the user is
|
||||
expected to use callbacks (or ``yield from``) to wait for the result.
|
||||
expected to use callbacks (or ``await/yield from``) to wait for the result.
|
||||
|
||||
All event loop methods specified as returning a coroutine are allowed
|
||||
to return either a Future or a coroutine, at the implementation's
|
||||
choice (the standard implementation always returns coroutines). All
|
||||
event loop methods documented as accepting coroutine arguments *must*
|
||||
accept both Futures and coroutines for such arguments. (A convenience
|
||||
function, ``async()``, exists to convert an argument that is either a
|
||||
function, ``ensure_future()``, exists to convert an argument that is either a
|
||||
coroutine or a Future into a Future.)
|
||||
|
||||
For users (like myself) who don't like using callbacks, a scheduler is
|
||||
provided for writing asynchronous I/O code as coroutines using the PEP
|
||||
380 ``yield from`` expressions. The scheduler is not pluggable;
|
||||
380 ``yield from`` or PEP 492 ``await`` expressions.
|
||||
The scheduler is not pluggable;
|
||||
pluggability occurs at the event loop level, and the standard
|
||||
scheduler implementation should work with any conforming event loop
|
||||
implementation. (In fact this is an important litmus test for
|
||||
|
@ -147,6 +148,15 @@ wait for a Future to complete by adding a callback to the Future.
|
|||
Likewise, the scheduler offers an operation to suspend a coroutine
|
||||
until a callback is called.
|
||||
|
||||
In case of any other async framework cannot just use Future and Task
|
||||
as is for some reason the framework may reimplement
|
||||
``loop.create_future()`` and ``loop.create_task()`` calls by returning
|
||||
an object which implements a superset of Future/Task interfaces but
|
||||
adds a new functionality required by the framework.
|
||||
|
||||
``loop.set_task_factory()`` may be useful as well for adopting asyncio
|
||||
tasks without implementing own event loop by a framework.
|
||||
|
||||
The event loop API provides limited interoperability with threads:
|
||||
there is an API to submit a function to an executor (see PEP 3148)
|
||||
which returns a Future that is compatible with the event loop, and
|
||||
|
@ -286,6 +296,8 @@ framework). The default event loop policy is an instance of the class
|
|||
``DefaultEventLoopPolicy``. The current event loop policy object can
|
||||
be retrieved by calling ``get_event_loop_policy()``.
|
||||
|
||||
TBD: decribe child watchers and UNIX quirks for subprocess processing
|
||||
|
||||
Passing an Event Loop Around Explicitly
|
||||
'''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
|
@ -361,7 +373,8 @@ stopping and closing. (However, a partially-conforming event loop is
|
|||
still better than nothing. :-)
|
||||
|
||||
- Starting, stopping and closing: ``run_forever()``,
|
||||
``run_until_complete()``, ``stop()``, ``is_running()``, ``close()``.
|
||||
``run_until_complete()``, ``stop()``, ``is_running()``, ``close()``,
|
||||
``is_closed()``.
|
||||
|
||||
- Basic and timed callbacks: ``call_soon()``, ``call_later()``,
|
||||
``call_at()``, ``time()``.
|
||||
|
@ -377,6 +390,14 @@ still better than nothing. :-)
|
|||
- Wrapped socket methods: ``sock_recv()``, ``sock_sendall()``,
|
||||
``sock_connect()``, ``sock_accept()``.
|
||||
|
||||
- Tasks and futures support: ``create_future()``, ``create_task``,
|
||||
``set_task_factory()``, ``get_task_factory()``.
|
||||
|
||||
- Error handling: ``get_exception_handler()``, ``set_exception_handler()``,
|
||||
``default_exception_handler()``, ``call_exception_handler()``.
|
||||
|
||||
- Debug mode: ``get_debug()``, ``set_debug()``.
|
||||
|
||||
The second set of categories *may* be supported by conforming event
|
||||
loop implementations. If not supported, they will raise
|
||||
``NotImplementedError``. (In the default implementation,
|
||||
|
@ -416,6 +437,8 @@ stopped. These methods deal with starting and stopping an event loop:
|
|||
Future is done. If the Future is done, its result is returned, or
|
||||
its exception is raised. This cannot be called when the event loop
|
||||
is already running.
|
||||
The method creates a new ``Task`` object if the
|
||||
parameter is a coroutine.
|
||||
|
||||
- ``stop()``. Stops the event loop as soon as it is convenient. It
|
||||
is fine to restart the loop with ``run_forever()`` or
|
||||
|
@ -438,6 +461,10 @@ stopped. These methods deal with starting and stopping an event loop:
|
|||
loop should not be used again. It may be called multiple times;
|
||||
subsequent calls are no-ops.
|
||||
|
||||
- ``is_closed()``. Returns ``True`` if the event loop is closed,
|
||||
``False`` otherwise.
|
||||
|
||||
|
||||
Basic Callbacks
|
||||
'''''''''''''''
|
||||
|
||||
|
@ -943,6 +970,69 @@ traceback. In some cases they are caught and re-raised. (Examples of
|
|||
this category include ``KeyboardInterrupt`` and ``SystemExit``; it is
|
||||
usually unwise to treat these the same as most other exceptions.)
|
||||
|
||||
The event loop passes caught but not handled exceptions into its
|
||||
*exception handler*. The handler is a callback which accepts
|
||||
*context* dict as a parameter::
|
||||
|
||||
def exception_handler(context):
|
||||
...
|
||||
|
||||
*context* may have many different keys but several of them are very
|
||||
widely used and present in almost all contexts:
|
||||
|
||||
- ``'message'``: error message.
|
||||
- ``'exception'``: exception instance, no exception on a stack if the
|
||||
value is ``None``.
|
||||
- ``'source_traceback'``: a list of strigs for stack trace on the
|
||||
failed object creation moment.
|
||||
- ``'handle_traceback'``: a list of strigs for stack trace on the
|
||||
failed handle creation moment.
|
||||
|
||||
The loop has the following methods related to exception handling:
|
||||
|
||||
- ``get_exception_handler()`` returns the current handler registered
|
||||
in the loop.
|
||||
|
||||
- ``set_exception_handler(handler)`` assings new handler.
|
||||
|
||||
- ``default_exception_handler(context)`` is *default* handler for the
|
||||
loop implementation.
|
||||
|
||||
- ``call_exception_handler(context)`` passes *context* into the
|
||||
registered exception handler. The call allows to handle uncaught
|
||||
exceptions uniformly by third-party libraries.
|
||||
|
||||
The loop uses ``default_exception_handler()`` if the default was not
|
||||
overridden by explicit ``set_exception_handler()`` call.
|
||||
|
||||
Debug Mode
|
||||
----------
|
||||
|
||||
By default the loop operates in *release* mode for sake of performance.
|
||||
But application may enable *debug* mode.
|
||||
|
||||
In this mode many additional checks are enabled, for example:
|
||||
|
||||
- source tracebacks are available for unrolled exceptions in futures/tasks.
|
||||
|
||||
- loop checks callback execution time for blaming blocking code.
|
||||
|
||||
``loop.slow_callback_duration`` attribute controls maximum execution
|
||||
time for callback or task iteration between two *yield points*. By
|
||||
default the attribute's value is ``0.1`` (100 milliseconds) but it
|
||||
may be tuned by user.
|
||||
|
||||
There are two methods related to the subject:
|
||||
|
||||
- ``get_debug()`` returns ``True`` if *debug* mode is enabled,
|
||||
``False`` otherwise.
|
||||
|
||||
- ``set_debug(enabled)`` enables *debug* mode if the parameter is ``True``.
|
||||
|
||||
Debug mode is switched on if ``PYTHONASYNCIODEBUG`` *environment
|
||||
variable* is defined and not empty.
|
||||
|
||||
|
||||
Handles
|
||||
-------
|
||||
|
||||
|
@ -1433,8 +1523,7 @@ methods:
|
|||
|
||||
- ``pipe_data_received(fd, data)``. Called when the subprocess writes
|
||||
data to its stdout or stderr. ``fd`` is the file descriptor (1 for
|
||||
stdout, 2 for stderr). ``data`` is a ``bytes`` object. (TBD: No
|
||||
``pipe_eof_received()``?)
|
||||
stdout, 2 for stderr). ``data`` is a ``bytes`` object.
|
||||
|
||||
- ``pipe_connection_lost(fd, exc)``. Called when the subprocess
|
||||
closes its stdin, stdout or stderr. ``fd`` is the file descriptor.
|
||||
|
@ -1647,8 +1736,8 @@ callback-based frameworks like Twisted. After converting a coroutine
|
|||
into a Task, callbacks can be added to the Task.
|
||||
|
||||
To convert a coroutine into a task, call the coroutine function and
|
||||
pass the resulting coroutine object to the ``asyncio.Task()``
|
||||
constructor. You may also use ``asyncio.async()`` for this purpose.
|
||||
pass the resulting coroutine object to the ``loop.create_task()``
|
||||
method. You may also use ``asyncio.ensure_future()`` for this purpose.
|
||||
|
||||
You may ask, why not automatically convert all coroutines to Tasks?
|
||||
The ``@asyncio.coroutine`` decorator could do this. However, this would
|
||||
|
@ -1656,6 +1745,22 @@ slow things down considerably in the case where one coroutine calls
|
|||
another (and so on), as switching to a "bare" coroutine has much less
|
||||
overhead than switching to a Task.
|
||||
|
||||
A Task is inherited from ``Future`` but adds new methods:
|
||||
|
||||
- ``current_task(loop=None)``. A *class method* returning the
|
||||
currently running task in an event loop. If *loop* is ``None`` the
|
||||
method returns a current task for default loop. Every coroutine is
|
||||
executed inside a *task context* either by running it by
|
||||
``ensure_future()`` / ``loop.create_task()`` call or by calling a
|
||||
nested coroutine by ``await nested()``. The method may return
|
||||
``None`` if it's called *outside* of coroutine, e.g. in callback
|
||||
scheduled by ``loop.call_later()``.
|
||||
|
||||
- ``all_tasks(loop=None)``. A *class method* returning a set of all
|
||||
active tasks for the loop. The method uses default loop if *loop* is
|
||||
``None``.
|
||||
|
||||
|
||||
The Scheduler
|
||||
-------------
|
||||
|
||||
|
@ -1930,6 +2035,8 @@ status allows revising these decisions for Python 3.5.)
|
|||
References
|
||||
==========
|
||||
|
||||
- PEP 492 describes the semantics of ``async/await``.
|
||||
|
||||
- PEP 380 describes the semantics of ``yield from``.
|
||||
|
||||
- Greg Ewing's ``yield from`` tutorials:
|
||||
|
|
Loading…
Reference in New Issue