Refactor run*() family, rename Handler->Handle, updated intros.
This commit is contained in:
parent
ca459300f3
commit
06c6f63510
145
pep-3156.txt
145
pep-3156.txt
|
@ -17,8 +17,8 @@ Python 3.3. Consider this the concrete proposal that is missing from
|
|||
PEP 3153. The proposal includes a pluggable event loop API, transport
|
||||
and protocol abstractions similar to those in Twisted, and a
|
||||
higher-level scheduler based on ``yield from`` (PEP 380). A reference
|
||||
implementation is in the works under the code name Tulip (the Tulip
|
||||
repo is linked from the References section at the end).
|
||||
implementation is in the works under the code name Tulip. The Tulip
|
||||
repo is linked from the References section at the end.
|
||||
|
||||
|
||||
Introduction
|
||||
|
@ -26,24 +26,33 @@ Introduction
|
|||
|
||||
The event loop is the place where most interoperability occurs. It
|
||||
should be easy for (Python 3.3 ports of) frameworks like Twisted,
|
||||
Tornado, or ZeroMQ to either adapt the default event loop
|
||||
Tornado, or even gevents to either adapt the default event loop
|
||||
implementation to their needs using a lightweight wrapper or proxy, or
|
||||
to replace the default event loop implementation with an adaptation of
|
||||
their own event loop implementation. (Some frameworks, like Twisted,
|
||||
have multiple event loop implementations. This should not be a
|
||||
problem since these all have the same interface.)
|
||||
|
||||
It should even be possible for two different third-party frameworks to
|
||||
interoperate, either by sharing the default event loop implementation
|
||||
(each using its own adapter), or by sharing the event loop
|
||||
implementation of either framework. In the latter case two levels of
|
||||
adaptation would occur (from framework A's event loop to the standard
|
||||
event loop interface, and from there to framework B's event loop).
|
||||
Which event loop implementation is used should be under control of the
|
||||
main program (though a default policy for event loop selection is
|
||||
provided).
|
||||
In most cases it should be possible for two different third-party
|
||||
frameworks to interoperate, either by sharing the default event loop
|
||||
implementation (each using its own adapter), or by sharing the event
|
||||
loop implementation of either framework. In the latter case two
|
||||
levels of adaptation would occur (from framework A's event loop to the
|
||||
standard event loop interface, and from there to framework B's event
|
||||
loop). Which event loop implementation is used should be under
|
||||
control of the main program (though a default policy for event loop
|
||||
selection is provided).
|
||||
|
||||
Thus, two separate APIs are defined:
|
||||
For this interoperability to be effective, the preferred direction of
|
||||
adaptation in third party frameworks is to keep the default event loop
|
||||
and adapt it to the framework's API. Ideally all third party
|
||||
frameworks would give up their own event loop implementation in favor
|
||||
of the standard implementation. But not all frameworks may be
|
||||
satisfied with the functionality provided by the standard
|
||||
implementation.
|
||||
|
||||
In order to support both directions of adaptation, two separate APIs
|
||||
are defined:
|
||||
|
||||
- getting and setting the current event loop object
|
||||
- the interface of a conforming event loop and its minimum guarantees
|
||||
|
@ -118,13 +127,6 @@ Details of the interfaces between transports and protocols are given
|
|||
later.
|
||||
|
||||
|
||||
Non-goals
|
||||
=========
|
||||
|
||||
Interoperability with systems like Stackless Python or
|
||||
greenlets/gevent is not a goal of this PEP.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
|
@ -195,35 +197,18 @@ delays are measured in seconds, and may be ints or floats. The
|
|||
accuracy and precision of the clock are up to the implementation; the
|
||||
default implementation uses ``time.monotonic()``.
|
||||
|
||||
A note about callbacks and Handlers: any function that takes a
|
||||
A note about callbacks and Handles: any function that takes a
|
||||
callback and a variable number of arguments for it can also be given a
|
||||
Handler object instead of the callback. Then no arguments should be
|
||||
given, and the Handler should represent an immediate callback (as
|
||||
Handle object instead of the callback. Then no arguments should be
|
||||
given, and the Handle should represent an immediate callback (as
|
||||
returned from ``call_soon()``), not a delayed callback (as returned
|
||||
from ``call_later()``). If the Handler is already cancelled, the
|
||||
from ``call_later()``). If the Handle is already cancelled, the
|
||||
call is a no-op.
|
||||
|
||||
A conforming event loop object has the following methods:
|
||||
|
||||
- ``run()``. Runs the event loop until there is nothing left to do.
|
||||
This means, in particular:
|
||||
|
||||
- No more calls scheduled with ``call_later()``,
|
||||
``call_repeatedly()``, ``call_soon()``, or
|
||||
``call_soon_threadsafe()``, except for cancelled calls.
|
||||
|
||||
- No more registered file descriptors. It is up to the registering
|
||||
party to unregister a file descriptor when it is closed.
|
||||
|
||||
Note: ``run()`` blocks until the termination condition is met,
|
||||
or until ``stop()`` is called.
|
||||
|
||||
Note: if you schedule a call with ``call_repeatedly()``, ``run()``
|
||||
will not exit until you cancel it.
|
||||
|
||||
TBD: How many variants of this do we really need?
|
||||
|
||||
- ``run_forever()``. Runs the event loop until ``stop()`` is called.
|
||||
- ``run()``. Runs the event loop until ``stop()`` is called. This
|
||||
cannot be called when the event loop is already running.
|
||||
|
||||
- ``run_until_complete(future, timeout=None)``. Runs the event loop
|
||||
until the Future is done. If a timeout is given, it waits at most
|
||||
|
@ -232,28 +217,12 @@ A conforming event loop object has the following methods:
|
|||
done, or if ``stop()`` is called, ``TimeoutError`` is raised (but
|
||||
the Future is not cancelled). This cannot be called when the event
|
||||
loop is already running.
|
||||
|
||||
Note: This API is most useful for tests and the like. It should not
|
||||
be used as a substitute for ``yield from future`` or other ways to
|
||||
wait for a Future (e.g. registering a done callback).
|
||||
|
||||
- ``run_once(timeout=None)``. Run the event loop for a little while.
|
||||
If a timeout is given, an I/O poll made will block at most that
|
||||
long; otherwise, an I/O poll is not constrained in time.
|
||||
|
||||
Note: Exactlly how much work this does is up to the implementation.
|
||||
One constraint: if a callback immediately schedules itself using
|
||||
``call_soon()``, causing an infinite loop, ``run_once()`` should
|
||||
still return.
|
||||
|
||||
- ``stop()``. Stops the event loop as soon as it is convenient. It
|
||||
is fine to restart the loop with ``run()`` (or one of its variants)
|
||||
subsequently.
|
||||
is fine to restart the loop with ``run()`` or ``run_until_complete()``
|
||||
subsequently; no scheduled callbacks will be lost if this happens.
|
||||
|
||||
Note: How soon exactly is up to the implementation. All immediate
|
||||
callbacks that were already scheduled to run before ``stop()`` is
|
||||
called must still be run, but callbacks scheduled after it is called
|
||||
(or scheduled to be run later) will not be run.
|
||||
Note: How soon the event loop stops is up to the implementation.
|
||||
|
||||
- ``close()``. Closes the event loop, releasing any resources it may
|
||||
hold, such as the file descriptor used by ``epoll()`` or
|
||||
|
@ -263,16 +232,23 @@ A conforming event loop object has the following methods:
|
|||
- ``call_later(delay, callback, *args)``. Arrange for
|
||||
``callback(*args)`` to be called approximately ``delay`` seconds in
|
||||
the future, once, unless cancelled. Returns
|
||||
a ``Handler`` object representing the callback, whose
|
||||
a ``Handle`` object representing the callback, whose
|
||||
``cancel()`` method can be used to cancel the callback.
|
||||
If ``delay`` is <= 0, this acts like ``call_soon()`` instead.
|
||||
Otherwise, callbacks scheduled for exactly the same time will be
|
||||
called in an undefined order.
|
||||
|
||||
- ``call_repeatedly(interval, callback, **args)``. Like ``call_later()``
|
||||
but calls the callback repeatedly, every ``interval`` seconds,
|
||||
until the ``Handler`` returned is cancelled. The first call is in
|
||||
``interval`` seconds.
|
||||
- ``call_repeatedly(interval, callback, **args)``. Like
|
||||
``call_later()`` but calls the callback repeatedly, every (approximately)
|
||||
``interval`` seconds, until the ``Handle`` returned is cancelled or
|
||||
the callback raises an exception. The first call is in
|
||||
approximately ``interval`` seconds. If for whatever reason the
|
||||
callback happens later than scheduled, subsequent callbacks will be
|
||||
delayed for (at least) the same amount. The ``interval`` must be > 0.
|
||||
|
||||
- ``call_soon(callback, *args)``. Equivalent to ``call_later(0,
|
||||
callback, *args)``.
|
||||
- ``call_soon(callback, *args)``. This schedules a callback to be
|
||||
called as soon as possible. It guarantees that callbacks are called
|
||||
in the order in which they were scheduled.
|
||||
|
||||
- ``call_soon_threadsafe(callback, *args)``. Like
|
||||
``call_soon(callback, *args)``, but when called from another thread
|
||||
|
@ -286,8 +262,8 @@ A conforming event loop object has the following methods:
|
|||
|
||||
- ``add_signal_handler(sig, callback, *args). Whenever signal ``sig``
|
||||
is received, arrange for ``callback(*args)`` to be called. Returns
|
||||
a ``Handler`` which can be used to cancel the signal callback.
|
||||
(Cancelling the handler causes ``remove_signal_handler()`` to be
|
||||
a ``Handle`` which can be used to cancel the signal callback.
|
||||
(Cancelling the handle causes ``remove_signal_handler()`` to be
|
||||
called the next time the signal arrives. Explicitly calling
|
||||
``remove_signal_handler()`` is preferred.)
|
||||
Specifying another callback for the same signal replaces the
|
||||
|
@ -298,13 +274,13 @@ A conforming event loop object has the following methods:
|
|||
signale (e.g. ``SIGKILL``), ``RuntimeError`` if this particular event
|
||||
loop instance cannot handle signals (since signals are global per
|
||||
process, only an event loop associated with the main thread can
|
||||
handle signals).
|
||||
handle signals). (TBD: Rename to ``set_signal_handler()``?)
|
||||
|
||||
- ``remove_signal_handler(sig)``. Removes the handler for signal
|
||||
``sig``, if one is set. Raises the same exceptions as
|
||||
``add_signal_handler()`` (except that it may return ``False``
|
||||
instead raising ``RuntimeError`` for uncatchable signals). Returns
|
||||
``True``e\ if a handler was removed successfully, ``False`` if no
|
||||
``True`` if a handler was removed successfully, ``False`` if no
|
||||
handler was set.
|
||||
|
||||
Some methods in the standard conforming interface return Futures:
|
||||
|
@ -417,25 +393,26 @@ i.e. no disk files.
|
|||
|
||||
- ``add_reader(fd, callback, *args)``. Arrange for
|
||||
``callback(*args)`` to be called whenever file descriptor ``fd`` is
|
||||
ready for reading. Returns a ``Handler`` object which can be
|
||||
ready for reading. Returns a ``Handle`` object which can be
|
||||
used to cancel the callback. Note that, unlike ``call_later()``,
|
||||
the callback may be called many times. Calling ``add_reader()``
|
||||
again for the same file descriptor implicitly cancels the previous
|
||||
callback for that file descriptor. Note: cancelling the handler
|
||||
may be delayed until the handler would be called. If you plan to
|
||||
callback for that file descriptor. Note: cancelling the handle
|
||||
may be delayed until the handle would be called. If you plan to
|
||||
close ``fd``, you should use ``remove_reader(fd)`` instead.
|
||||
(TBD: Change this to raise an exception if a handler
|
||||
is already set.)
|
||||
(TBD: Change this to raise an exception if a handle
|
||||
is already set.) (TBD: Rename to ``set_reader()``?)
|
||||
|
||||
- ``add_writer(fd, callback, *args)``. Like ``add_reader()``,
|
||||
but registers the callback for writing instead of for reading.
|
||||
(TBD: Rename to ``set_reader()``?)
|
||||
|
||||
- ``remove_reader(fd)``. Cancels the current read callback for file
|
||||
descriptor ``fd``, if one is set. A no-op if no callback is
|
||||
currently set for the file descriptor. (The reason for providing
|
||||
this alternate interface is that it is often more convenient to
|
||||
remember the file descriptor than to remember the ``Handler``
|
||||
object.) Returns ``True`` if a handler was removed, ``False``
|
||||
remember the file descriptor than to remember the ``Handle``
|
||||
object.) Returns ``True`` if a callback was removed, ``False``
|
||||
if not.
|
||||
|
||||
- ``remove_writer(fd)``. This is to ``add_writer()`` as
|
||||
|
@ -522,13 +499,13 @@ traceback. (Examples of this category include ``KeyboardInterrupt``
|
|||
and ``SystemExit``; it is usually unwise to treat these the same as
|
||||
most other exceptions.)
|
||||
|
||||
The Handler Class
|
||||
-----------------
|
||||
The Handle Class
|
||||
----------------
|
||||
|
||||
The various methods for registering callbacks (e.g. ``call_later()``)
|
||||
all return an object representing the registration that can be used to
|
||||
cancel the callback. For want of a better name this object is called
|
||||
a ``Handler``, although the user never needs to instantiate
|
||||
a ``Handle``, although the user never needs to instantiate
|
||||
instances of this class. There is one public method:
|
||||
|
||||
- ``cancel()``. Attempt to cancel the callback.
|
||||
|
@ -597,7 +574,7 @@ public API is as follows, indicating the differences with PEP 3148:
|
|||
``call_soon()``. Note that the callback (unlike all other callbacks
|
||||
defined in this PEP, and ignoring the convention from the section
|
||||
"Callback Style" below) is always called with a single argument, the
|
||||
Future object, and should not be a Handler object.
|
||||
Future object, and should not be a Handle object.
|
||||
|
||||
- ``set_result(result)``. The Future must not be done (nor cancelled)
|
||||
already. This makes the Future done and schedules the callbacks.
|
||||
|
|
Loading…
Reference in New Issue