[Python-checkins] peps: Major update of the event loop spec.

guido.van.rossum python-checkins at python.org
Fri Apr 26 23:17:17 CEST 2013


http://hg.python.org/peps/rev/9c4e5f027fcc
changeset:   4862:9c4e5f027fcc
user:        Guido van Rossum <guido at python.org>
date:        Fri Apr 26 14:16:59 2013 -0700
summary:
  Major update of the event loop spec.

files:
  pep-3156.txt |  870 ++++++++++++++++++++++++++------------
  1 files changed, 600 insertions(+), 270 deletions(-)


diff --git a/pep-3156.txt b/pep-3156.txt
--- a/pep-3156.txt
+++ b/pep-3156.txt
@@ -92,21 +92,25 @@
 difference between transports and protocols is in order.  At the
 highest level, the transport is concerned with *how* bytes are
 transmitted, while the protocol determines *which* bytes to transmit
-(and when).
+(and to some extent when).
 
-A transport represents a pair of streams (one in each direction) that
-each transmit a sequence of bytes.  The most common transport is
-probably the TCP connection.  Another common transport is SSL.  But
-there are some other things that can be viewed as transports, for
-example an SSH session or a pair of UNIX pipes.  Typically there
-aren't many different transport implementations, and most of them
-come with the event loop implementation.
+The most common type of transport is a bidirectional stream transport.
+It represents a pair of streams (one in each direction) that each
+transmit a sequence of bytes.  The most common example of a
+bidirectional stream transport is probably a TCP connection.  Another
+common example is an SSL connection.  But there are some other things
+that can be viewed this way, for example an SSH session or a pair of
+UNIX pipes.  Typically there aren't many different transport
+implementations, and most of them come with the event loop
+implementation.  Note that transports don't need to use sockets, not
+even if they use TCP -- sockets are a platform-specific implementation
+detail.
 
-A transport has two "sides": one side talks to the network (or the
-subprocess, or whatever low-level interface it wraps), and the other
-side talks to the protocol.  The former uses whatever API is necessary
-to implement the transport; but the interface between transport and
-protocol is standardized by this PEP.
+A bidirectional stream transport has two "sides": one side talks to
+the network (or another process, or whatever low-level interface it
+wraps), and the other side talks to the protocol.  The former uses
+whatever API is necessary to implement the transport; but the
+interface between transport and protocol is standardized by this PEP.
 
 A protocol represents some kind of "application-level" protocol such
 as HTTP or SMTP.  Its primary interface is with the transport.  While
@@ -115,16 +119,16 @@
 have libraries of useful 3rd party protocol implementations that can
 be downloaded and installed from pypi.python.org.
 
-There is also a somewhat more general notion of transport and
-protocol, where the transport wraps some other communication
-abstraction.  Example include an interface for sending and receiving
-datagrams, or a subprocess manager.  The separation of concerns is the
-same as for stream transports and protocols, but the specific
-interface between transport and protocol can be different in each
-case.
+There general notion of transport and protocol includes other
+interfaces, where the transport wraps some other communication
+abstraction.  Examples include interfaces for sending and receiving
+datagrams (e.g. UDP), or a subprocess manager.  The separation of
+concerns is the same as for bidirectional stream transports and
+protocols, but the specific interface between transport and protocol
+is different in each case.
 
-Details of the interfaces between transports and protocols are given
-later.
+Details of the interfaces defined by the various standard types of
+transports and protocols are given later.
 
 
 Specification
@@ -133,9 +137,11 @@
 Dependencies
 ------------
 
-Python 3.3 is required.  No new language or standard library features
-beyond Python 3.3 are required.  No third-party modules or packages
-are required.
+Python 3.3 is required for many of the proposed features.  The
+reference implementation (Tulip) requires no new language or standard
+library features beyond Python 3.3, no third-party modules or
+packages, and no C code, except for the proactor-based event loop on
+Windows.
 
 Module Namespace
 ----------------
@@ -155,57 +161,169 @@
 toplevel package name.  Classes and functions given without a module
 name are assumed to be accessed via the toplevel package.
 
-Event Loop Policy: Getting and Setting the Event Loop
------------------------------------------------------
+Event Loop Policy: Getting and Setting the Current Event Loop
+-------------------------------------------------------------
 
-To get the current event loop, use ``get_event_loop()``.  This returns
-an instance of the ``EventLoop`` class defined below or an equivalent
-object.  It is possible that ``get_event_loop()`` returns a different
-object depending on the current thread, or depending on some other
-notion of context.
+Event loop management is controlled by an event loop policy, which is
+a global (per-process) state.  There is a default policy, and an API
+to change the policy.  The policy defines the notion of context; the
+default policy's notion of context is defined as the current thread.
 
-To set the current event loop, use ``set_event_loop(event_loop)``,
-where ``event_loop`` is an instance of the ``EventLoop`` class or
-equivalent.  This uses the same notion of context as
-``get_event_loop()``.
-
-For the benefit of unit tests and other special cases there's a third
-policy function: ``new_event_loop()``, which creates and returns a new
-EventLoop instance according to the policy's default rules.  To make
-this the current event loop, you must call ``set_event_loop()``.
-
-To change the way the above three functions work
-(including their notion of context), call
-``set_event_loop_policy(policy)``, where ``policy`` is an event loop
-policy object.  The policy object can be any object that has methods
-``get_event_loop()``, ``set_event_loop(event_loop)``
-and ``new_event_loop()`` behaving like
-the functions described above.  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()``.
+Certain platforms or programming frameworks may change the default
+policy to something more suitable to the expectations of the users of
+that platform or framework.  Such platforms or frameworks must
+document their policy and at what point during their initialization
+sequence the policy is set. in order to avoid undefined behavior when
+multiple active frameworks want to override the default policy.
 
 An event loop policy may but does not have to enforce that there is
 only one event loop in existence.  The default event loop policy does
 not enforce this, but it does enforce that there is only one event
-loop per thread.
+loop per thread (as far as ``get_event_loop()`` is concerned).
 
-Event Loop Interface
---------------------
+To get the current event loop, use ``get_event_loop()``.  This returns
+an event loop object implementing the interface specified below, or
+``None`` in case no current event loop has been set and the current
+policy does not specify how to create one for the current context.  It
+is expected that ``get_event_loop()`` returns a different object
+depending on the context, and the default policy will only create a
+default event loop in the main thread; in other threads an event loop
+must be explicitly set (but other policies may behave differently).
+Event loop creation is lazy; i.e. the first call to
+``get_event_loop()`` creates an event loop instance if necessary and
+specified by the current policy.
+
+To set the current event loop, use ``set_event_loop(event_loop)``,
+where ``event_loop`` is an event loop object.  It is allowed to set
+the current event loop to ``None`` (although under the default policy,
+if the main thread's current event loop is set to ``None``, and
+``get_event_loop()`` is called subsequently, it will create a new
+event loop instance.
+
+For the benefit of unit tests and other special cases there's a third
+policy function: ``new_event_loop()``, which creates and returns a new
+event loop object according to the policy's default rules.  To make
+this the current event loop, you must call ``set_event_loop()``.
+
+To change the event loop policy, call
+``set_event_loop_policy(policy)``, where ``policy`` is an event loop
+policy object or ``None``.  The policy object must be an object that
+has methods ``get_event_loop()``, ``set_event_loop(loop)`` and
+``new_event_loop()``, all behaving like the functions described above.
+Passing a policy value of ``None`` restores the default event loop
+policy (overriding the alternate default set by the platform or
+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: Require
+inheriting from ``AbstractEventLoopPolicy``?)
+
+Notes for the Event Loop Interface
+----------------------------------
 
 A note about times: as usual in Python, all timeouts, intervals and
 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 Handles: any function that takes a
-callback and a variable number of arguments for it can also be given a
-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 Handle is already cancelled, the
-call is a no-op.
+A note about callbacks and Handles: any function in the API described
+below that takes a callback and a variable number of arguments for it
+can also be given a 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 Handle has
+been cancelled, the call is a no-op.
 
-A conforming event loop object has the following methods:
+Event Loop Classes
+------------------
+
+There is no actual class named ``EventLoop``.  There is an
+``AbstractEventLoop`` class which defines all the methods without
+implementations, and serves primarily as documentation.  The following
+concrete classes are defined:
+
+- ``SelectorEventLoop`` is a concrete implementation of the full API
+  based on the ``selectors`` module.  (This module is part of Tulip,
+  but not specified by this PEP.  It is separately proposed for
+  inclusion in the standard library.)  The constructor takes one
+  optional argument, a ``selectors.Selector`` object.  By default an
+  instance of ``selectors.DefaultSelector`` is created and used.
+
+- ``ProactorEventLoop`` is a concrete implementation of the API except
+  for the I/O event handling and signal handling methods.  It is only
+  defined on Windows (or on other platforms which support a similar
+  API for "overlapped I/O").  The constructor takes one optional
+  argument, a ``Proactor`` object.  By default an instance of
+  ``IocpProactor`` is created and used.  (The ``IocpProactor`` class
+  is not specified by this PEP.  Its inclusion in the standard library
+  is not currently under consideration; it is just an implementation
+  detail of the ``ProactorEventLoop`` class.
+
+Event Loop Methods Overview
+---------------------------
+
+The methods of a conforming event loop are grouped into several
+categories.  A brief overview of the categories.  The first set of
+categories must be supported by all conforming event loop
+implementations.   (However, in some cases a partially-conforming
+implementation may choose not to implement the internet/socket
+methods, and still conform to the other methods.)
+
+- Resource management: ``close()``.
+
+- Starting and stopping: ``run_forever()``, ``run_until_complete()``,
+  ``stop()``, ``is_running()``.
+
+- Basic callbacks: ``call_soon()``, ``call_later()``,
+  ``call_repeatedly()``.
+
+- Thread interaction: ``call_soon_threadsafe()``,
+  ``wrap_future()``, ``run_in_executor()``,
+  ``set_default_executor()``.
+
+- Internet name lookups: ``getaddrinfo()``, ``getnameinfo()``.
+
+- Internet connections: ``create_connection()``, ``start_serving()``,
+  ``stop_serving()``, ``create_datagram_endpoint()``.
+
+- Wrapped socket methods: ``sock_recv()``, ``sock_sendall()``,
+  ``sock_connect()``, ``sock_accept()``.
+
+The second set of categories *may* be supported by conforming event
+loop implementations.  If not supported, they will raise
+``NotImplementedError``.  (In the current state of Tulip,
+``SelectorEventLoop`` on UNIX systems supports all of these;
+``SelectorEventLoop`` on Windows supports the I/O event handling
+category; ``ProactorEventLoop`` on Windows supports None.  The
+intention is to add support for pipes and subprocesses on Windows as
+well, using the ``subprocess`` module in the standard library.)
+
+- I/O callbacks: ``add_reader()``, ``remove_reader()``,
+  ``add_writer()``, ``remove_writer()``.
+
+- Pipes and subprocesses: ``connect_read_pipe()``,
+  ``connect_write_pipe()``, ``spawn_subprocess()``.
+
+- Signal callbacks: ``add_signal_handler()``,
+  ``remove_signal_handler()``.
+
+Required Event Loop Methods
+---------------------------
+
+Resource Management
+'''''''''''''''''''
+
+- ``close()``.  Closes the event loop, releasing any resources it may
+  hold, such as the file descriptor used by ``epoll()`` or
+  ``kqueue()``.  This should not be called while the event loop is
+  running.  After it has been called the event loop may not be used
+  again.  It may be called multiple times; subsequent calls are
+  no-ops.
+
+Starting and Stopping
+'''''''''''''''''''''
+
+An (unclosed) event loop can be in one of two states: running or
+stopped.  These methods deal with starting and stopping an event loop:
 
 - ``run_forever()``.  Runs the event loop until ``stop()`` is called.
   This cannot be called when the event loop is already running.  (This
@@ -224,20 +342,27 @@
   loop is already running.
 
 - ``stop()``.  Stops the event loop as soon as it is convenient.  It
-  is fine to restart the loop with ``run()`` or ``run_until_complete()``
-  subsequently; no scheduled callbacks will be lost if this happens.
-  Note: ``stop()`` returns normally and the current callback is
-  allowed to continue.  How soon after this point the event loop stops
-  is up to the implementation, but the intention is to stop short of
-  polling for I/O, and not to run any callbacks scheduled in the
-  future; the major freedom an implementation has is how much of the
-  "ready queue" (callbacks already scheduled with ``call_soon()``) it
-  processes before stopping.
+  is fine to restart the loop with ``run_forever()`` or
+  ``run_until_complete()`` subsequently; no scheduled callbacks will
+  be lost if this is done.  Note: ``stop()`` returns normally and the
+  current callback is allowed to continue.  How soon after this point
+  the event loop stops is up to the implementation, but the intention
+  is to stop short of polling for I/O, and not to run any callbacks
+  scheduled in the future; the major freedom an implementation has is
+  how much of the "ready queue" (callbacks already scheduled with
+  ``call_soon()``) it processes before stopping.
 
-- ``close()``.  Closes the event loop, releasing any resources it may
-  hold, such as the file descriptor used by ``epoll()`` or
-  ``kqueue()``.  This should not be called while the event loop is
-  running.  It may be called multiple times.
+- ``is_running()``.  Returns ``True`` if the event loop is currently
+  running, ``False`` if it is stopped.  (TBD: Do we need another
+  inquiry method to tell whether the loop is in the process of
+  stopping?)
+
+Basic Callbacks
+'''''''''''''''
+
+- ``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_later(delay, callback, *args)``.  Arrange for
   ``callback(*args)`` to be called approximately ``delay`` seconds in
@@ -256,19 +381,314 @@
   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)``.  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.
+Thread interaction
+''''''''''''''''''
 
 - ``call_soon_threadsafe(callback, *args)``.  Like
   ``call_soon(callback, *args)``, but when called from another thread
   while the event loop is blocked waiting for I/O, unblocks the event
   loop.  This is the *only* method that is safe to call from another
-  thread.  (To schedule a callback for a
-  later time in a threadsafe manner, you can use
-  ``ev.call_soon_threadsafe(ev.call_later, when, callback, *args)``.)
-  This is not safe to call from a signal handler (since it may use
-  locks).
+  thread.  (To schedule a callback for a later time in a threadsafe
+  manner, you can use ``loop.call_soon_threadsafe(loop.call_later,
+  when, callback, *args)``.)  This is not safe to call from a signal
+  handler (since it may use locks).
+
+- ``wrap_future(future)``.  This takes a PEP 3148 Future (i.e., an
+  instance of ``concurrent.futures.Future``) and returns a Future
+  compatible with the event loop (i.e., a ``tulip.Future`` instance).
+
+- ``run_in_executor(executor, callback, *args)``.  Arrange to call
+  ``callback(*args)`` in an executor (see PEP 3148).  Returns a Future
+  whose result on success is the return value of that call.  This is
+  equivalent to ``wrap_future(executor.submit(callback, *args))``.  If
+  ``executor`` is ``None``, the default executor set by
+  ``set_default_executor()`` is used.  If no default executor has been
+  set yet, a ``ThreadPoolExecutor`` with 5 threads is created and set
+  as the default executor.
+
+- ``set_default_executor(executor)``.  Set the default executor used
+  by ``run_in_executor()``.  The argument must be a PEP 3148
+  ``Executor`` instance or ``None``, in order to reset the default
+  executor.
+
+Internet name lookups
+'''''''''''''''''''''
+
+These methods are useful if you want to connect or bind a socket to an
+address without the risk of blocking for the name lookup.  They are
+usually called implicitly by ``create_connection()``,
+``start_serving()`` or ``create_datagram_endpoint()``.
+
+- ``getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)``.
+  Similar to the ``socket.getaddrinfo()`` function but returns a
+  Future.  The Future's result on success will be a list of the same
+  format as returned by ``socket.getaddrinfo()``, i.e. a list of
+  ``(address_family, socket_type, socket_protocol, canonical_name,
+  address)`` where ``address`` is a 2-tuple ``(ipv4_address, port)``
+  for IPv4 addresses and a 4-tuple ``(ipv4_address, port, flow_info,
+  scope_id)`` for IPv6 addresses.  If the ``family`` argument is zero
+  or unspecified, the list returned may contain a mixture of IPv4 and
+  IPv6 addresses; otherwise the addresses returned are constrained by
+  the ``family`` value (similar for ``proto`` and ``flags``).  The
+  default implementation calls ``socket.getaddrinfo()`` using
+  ``run_in_executor()``, but other implementations may choose to
+  implement their own DNS lookup.  The optional arguments *must* be
+  specified as keyword arguments.
+
+- ``getnameinfo(sockaddr, flags=0)``.  Similar to
+  ``socket.getnameinfo()`` but returns a Future.  The Future's result
+  on success will be a tuple ``(host, port)``.  Same implementation
+  remarks as for ``getaddrinfo()``.
+
+Internet connections
+''''''''''''''''''''
+
+These are the high-level interfaces for managing internet connections.
+Their use is recommended over the corresponding lower-level interfaces
+because they abstract away the differences between selector-based
+and proactor-based event loops.
+
+Note that the client and server side of stream connections use the
+same transport and protocol interface.  However, datagram endpoints
+use a different transport and protocol interface.
+
+- ``create_connection(protocol_factory, host, port, **kwargs)``.
+  Creates a stream connection to a given internet host and port.  This
+  is a task that is typically called from the client side of the
+  connection.  It creates an implementation-dependent (bidirectional
+  stream) Transport to represent the connection, then calls
+  ``protocol_factory()`` to instantiate (or retrieve) the user's
+  Protocol implementation, and finally ties the two together.  (See
+  below for the definitions of Transport and Protocol.)  The user's
+  Protocol implementation is created or retrieved by calling
+  ``protocol_factory()`` without arguments(*).  The coroutine's result
+  on success is the ``(transport, protocol)`` pair; if a failure
+  prevents the creation of a successful connection, an appropriate
+  exception will be raised.  Note that when the coroutine completes,
+  the protocol's ``connection_made()`` method has not yet been called;
+  that will happen when the connection handshake is complete.
+
+  (*) There is no requirement that ``protocol_factory`` is a class.
+  If your protocol class needs to have specific arguments passed to
+  its constructor, you can use ``lambda`` or ``functools.partial()``.
+  You can also pass a trivial ``lambda`` that returns a previously
+  constructed Protocol instance.
+
+  Optional keyword arguments:
+
+  - ``ssl``: Pass ``True`` to create an SSL transport (by default a
+    plain TCP transport is created).  Or pass an ``ssl.SSLContext``
+    object to override the default SSL context object to be used.
+
+  - ``family``, ``proto``, ``flags``: Address family, protocol and
+    flags to be passed through to ``getaddrinfo()``.  These all
+    default to ``0``, which means "not specified".  (The socket type
+    is always ``SOCK_STREAM``.)  If any of these values are not
+    specified, the ``getaddrinfo()`` method will choose appropriate
+    values.  Note: ``proto`` has nothing to do with the high-level
+    Protocol concept or the ``protocol_factory`` argument.
+
+  - ``sock``: An optional socket to be used instead of using the
+    ``host``, ``port``, ``family``, ``proto``, and ``flags``
+    arguments.  If this is given, host and port must be omitted;
+    otherwise, host and port are required.
+
+- ``start_serving(protocol_factory, host, port, **kwds)``.  Enters a
+  serving loop that accepts connections.  This is a Task that
+  completes once the serving loop is set up to serve.  The return
+  value is a list of one or more sockets in listening mode.  (Multiple
+  sockets may be returned if the specified address allows both IPv4
+  and IPv6 connections.)  You can use ``stop_serving()`` to stop the
+  serving loop.  Each time a connection is accepted,
+  ``protocol_factory`` is called without arguments(*) to create a
+  Protocol, a (bidirectional stream) Transport is created to represent
+  the network side of the connection, and the two are tied together by
+  calling ``protocol.connection_made(transport)``.
+
+  (*) See footnote above for ``create_connection()``.  However, since
+  ``protocol_factory()`` is called once for each new incoming
+  connection, it should return a new Protocol object each time it is
+  called.
+
+  Optional keyword arguments:
+
+  - ``ssl``: Pass ``True`` to create an SSL transport (by default a
+    plain TCP transport is created).  Or pass an ``ssl.SSLContext``
+    object to override the default SSL context object to be used.
+
+  - ``backlog``: Backlog value to be passed to the ``listen()`` call.
+    Defaults to ``100``.
+
+  - ``reuse_address``: Whether to set the ``SO_REUSEADDR`` option on
+    the socket.  The default is ``True`` on UNIX, ``False`` on
+    Windows.
+
+  - ``family``, ``flags``: Address family and flags to be passed
+     through to ``getaddrinfo()``.  The family defaults to
+     ``AF_UNSPEC``; the flags default to ``AI_PASSIVE``.  (The socket
+     type is always ``SOCK_STREAM``; the socket protocol always set to
+     ``0``, to let ``getaddrinfo()`` choose.)
+
+  - ``sock``: An optional socket to be used instead of using the
+    ``host``, ``port``, ``family``, and ``flags`` arguments.  If this
+    is given, host and port must be omitted; otherwise, host and port
+    are required.  The return value will be the one-element list
+    ``[sock]``.
+
+- ``stop_serving(sock)``.  The argument should be a socket from the
+  list returned by ``start_serving()``.  The serving loop associated
+  with that socket will be stopped.  Connections that have already
+  been accepted will not be affected.
+
+- ``create_datagram_endpoint(protocol_factory, local_addr,
+  remote_addr, **kwds)``.  Creates an endpoint for sending and
+  receiving datagrams (typically UDP packets).  Because of the nature
+  of datagram traffic, there are no separate calls to set up client
+  and server side, since usually a single endpoint acts as both client
+  and server.  This is a coroutine that returns a ``(transport,
+  protocol)`` pair on success, or raises an exception on failure.  If
+  the coroutine returns successfully, the transport will call
+  callbacks on the protocol whenever a datagram is received or the
+  socket is closed; it is up to the protocol to call methods on the
+  protocol to send datagrams.  Note that the transport and protocol
+  interfaces used here are different than those for stream
+  connections.
+
+  Arguments:
+
+  - ``protocol_factory``: A class or factory function that will be
+    called, without arguments, to construct the protocol object to be
+    returned.  The interface between datagram transport and protocol
+    is described below.
+
+  - ``local_addr``: An optional tuple indicating the address to which
+    the socket will be bound.  If given this must be a ``(host,
+    port)`` pair.  It will be passed to ``getaddrinfo()`` to be
+    resolved and the result will be passed to the ``bind()`` method of
+    the socket created.  If ``getaddrinfo()`` returns more than one
+    address, they will be tried in turn.  If omitted, no ``bind()``
+    call will be made.
+
+  - ``remote_addr``: An optional tuple indicating the address to which
+    the socket will be "connected".  (Since there is no such thing as
+    a datagram connection, this just specifies a default value for the
+    destination address of outgoing datagrams.)  If given this must be
+    a ``(host, port)`` pair.  It will be passed to ``getaddrinfo()``
+    to be resolved and the result will be passed to ``sock_connect()``
+    together with the socket created.  If ``getaddrinfo()`` returns
+    more than one address, they will be tried in turn.  If omitted,
+    no ``sock_connect()`` will be made.
+
+  - ``family``, ``proto``, ``flags``: Address family, protocol and
+    flags to be passed through to ``getaddrinfo()``.  These all
+    default to ``0``, which means "not specified".  (The socket type
+    is always ``SOCK_DGRAM``.)  If any of these values are not
+    specified, the ``getaddrinfo()`` method will choose appropriate
+    values.
+
+  Note that if both ``local_addr`` and ``remote_addr`` are present,
+  all combinations of local and remote addresses with matching address
+  family will be tried.
+
+Wrapped Socket Methods
+''''''''''''''''''''''
+
+The following methods for doing async I/O on sockets are not for
+general use.  They are primarily meant for transport implementations
+working with IOCP through the ``ProactorEventLoop`` class.  However,
+they are easily implementable for other event loop types, so there is
+no reason not to require them.  The socket argument has to be a
+non-blocking socket.
+
+- ``sock_recv(sock, n)``.  Receive up to ``n`` bytes from socket
+  ``sock``.  Returns a Future whose result on success will be a
+  bytes object.
+
+- ``sock_sendall(sock, data)``.  Send bytes ``data`` to socket
+  ``sock``.  Returns a Future whose result on success will be
+  ``None``.  Note: the name uses ``sendall`` instead of ``send``, to
+  reflect that the semantics and signature of this method echo those
+  of the standard library socket method ``sendall()`` rather than
+  ``send()``.  (TBD: but maybe it would be better to emulate
+  ``send()`` after all?  That would be better for datagram sockets.)
+
+- ``sock_connect(sock, address)``.  Connect to the given address.
+  Returns a Future whose result on success will be ``None``.
+
+- ``sock_accept(sock)``.  Accept a connection from a socket.  The
+  socket must be in listening mode and bound to an address.  Returns a
+  Future whose result on success will be a tuple ``(conn, peer)``
+  where ``conn`` is a connected non-blocking socket and ``peer`` is
+  the peer address.
+
+Optional Event Loop Methods
+---------------------------
+
+I/O Callbacks
+'''''''''''''
+
+These methods are primarily meant for transport implementations
+working with a selector.  They are implemented by
+``SelectorEventLoop`` but not by ``ProactorEventLoop``.  Custom event
+loop implementations may or may not implement them.
+
+The ``fd`` arguments below may be integer file descriptors, or
+"file-like" objects with a ``fileno()`` method that wrap integer file
+descriptors.  Not all file-like objects or file descriptors are
+acceptable.  Sockets (and socket file descriptors) are always
+accepted.  On Windows no other types are supported.  On UNIX, pipes
+and possibly tty devices are also supported, but disk files are not.
+Exactly which special file types are supported may vary by platform
+and per selector implementation.  (Experimentally, there is at least
+one kind of pseudo-tty on OSX that is supported by ``select`` and
+``poll`` but not by ``kqueue``: it is used by Emacs shell windows.)
+
+- ``add_reader(fd, callback, *args)``.  Arrange for
+  ``callback(*args)`` to be called whenever file descriptor ``fd`` is
+  deemed 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 implies a call to
+  ``remove_reader()`` for the same file descriptor.  Note that,
+  although cancelling the ``Handle`` returned is nearly equivalent to
+  calling ``remove_reader()``, it is strongly preferred to call
+  ``remove_reader()``.
+
+- ``add_writer(fd, callback, *args)``.  Like ``add_reader()``,
+  but registers the callback for writing instead of for reading.
+
+- ``remove_reader(fd)``.  Cancels the current read callback for file
+  descriptor ``fd``, if one is set.  If no callback is currently set
+  for the file descriptor, this is a no-op and returns ``False``.
+  Otherwise, it removes the callback arrangement, cancels the
+  corresponding ``Handle``, and returns ``True``.
+
+- ``remove_writer(fd)``.  This is to ``add_writer()`` as
+  ``remove_reader()`` is to ``add_reader()``.
+
+Pipes and Subprocesses
+''''''''''''''''''''''
+
+- ``connect_read_pipe(protocol_factory, pipe)``: Create a
+  unidrectional stream connection from a file-like object wrapping the
+  read end of a UNIX pipe.  The protocol/transport interface is the
+  read half of the bidirectional stream interface.
+
+- ``connect_write_pipe(protocol_factory, pipe)``: Create a
+  unidrectional stream connection from a file-like object wrapping the
+  write end of a UNIX pipe.  The protocol/transport interface is the
+  write half of the bidirectional stream interface.
+
+- TBD: A way to run a subprocess with stdin, stdout and stderr
+  connected to pipe transports.  (This is being designed but not yet
+  ready.)
+
+TBD: offer the same interface on Windows for e.g. named pipes.  (This
+should be possible given that the standard library ``subprocess``
+module is supported on Windows.)
+
+Signal callbacks
+''''''''''''''''
 
 - ``add_signal_handler(sig, callback, *args).  Whenever signal ``sig``
   is received, arrange for ``callback(*args)`` to be called.  Returns
@@ -284,7 +704,7 @@
   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).  (TBD: Rename to ``set_signal_handler()``?)
+  handle signals).
 
 - ``remove_signal_handler(sig)``.  Removes the handler for signal
   ``sig``, if one is set.  Raises the same exceptions as
@@ -293,176 +713,8 @@
   ``True`` if a handler was removed successfully, ``False`` if no
   handler was set.
 
-Some methods in the standard conforming interface return Futures:
-
-- ``wrap_future(future)``.  This takes a PEP 3148 Future (i.e., an
-  instance of ``concurrent.futures.Future``) and returns a Future
-  compatible with the event loop (i.e., a ``tulip.Future`` instance).
-
-- ``run_in_executor(executor, callback, *args)``.  Arrange to call
-  ``callback(*args)`` in an executor (see PEP 3148).  Returns a Future
-  whose result on success is the return value that call.  This is
-  equivalent to ``wrap_future(executor.submit(callback, *args))``.  If
-  ``executor`` is ``None``, a default ``ThreadPoolExecutor`` with 5
-  threads is used.
-
-- ``set_default_executor(executor)``.  Set the default executor used
-  by ``run_in_executor()``.
-
-- ``getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)``.
-  Similar to the ``socket.getaddrinfo()`` function but returns a
-  Future.  The Future's result on success will be a list of the same
-  format as returned by ``socket.getaddrinfo()``.  The default
-  implementation calls ``socket.getaddrinfo()`` using
-  ``run_in_executor()``, but other implementations may choose to
-  implement their own DNS lookup.  The optional arguments *must* be
-  specified as keyword arguments.
-
-- ``getnameinfo(sockaddr, flags=0)``.  Similar to
-  ``socket.getnameinfo()`` but returns a Future.  The Future's result
-  on success will be a tuple ``(host, port)``.  Same implementation
-  remarks as for ``getaddrinfo()``.
-
-- ``create_connection(protocol_factory, host, port, **kwargs)``.
-  Creates a stream connection to a given host and port.  This creates
-  an implementation-dependent Transport to represent the connection,
-  then calls ``protocol_factory()`` to instantiate (or retrieve) the
-  user's Protocol implementation, and finally ties the two together.
-  (See below for the definitions of Transport and Protocol.)  The
-  user's Protocol implementation is created or retrieved by calling
-  ``protocol_factory()`` without arguments(*).  The return value is a
-  Future whose result on success is the ``(transport, protocol)``
-  pair; if a failure prevents the creation of a successful connection,
-  the Future will have an appropriate exception set.  Note that when
-  the Future completes, the protocol's ``connection_made()`` method
-  has not yet been called; that will happen when the connection
-  handshake is complete.
-
-  (*) There is no requirement that ``protocol_factory`` is a class.
-  If your protocol class needs to have specific arguments passed to
-  its constructor, you can use ``lambda`` or ``functools.partial()``.
-  You can also pass a trivial ``lambda`` that returns a previously
-  constructed Protocol instance.
-
-  Optional keyword arguments:
-
-  - ``family``, ``proto``, ``flags``: Address familty,
-    protcol, and miscellaneous flags to be passed through
-    to ``getaddrinfo()``.  These all default to ``0``.
-    (The socket type is always ``SOCK_STREAM``.)
-
-  - ``ssl``: Pass ``True`` to create an SSL transport (by default a
-    plain TCP is created).  Or pass an ``ssl.SSLContext`` object to
-    override the default SSL context object to be used.
-
-- ``start_serving(protocol_factory, host, port, **kwds)``.  Enters a
-  loop that accepts connections.  Returns a Future that completes once
-  the loop is set up to serve; its return value is None.  Each time a
-  connection is accepted, ``protocol_factory`` is called without
-  arguments(*) to create a Protocol, a Transport is created to represent
-  the network side of the connection, and the two are tied together by
-  calling ``protocol.connection_made(transport)``.
-
-  (*) See footnote above for ``create_connection()``.  However, since
-  ``protocol_factory()`` is called once for each new incoming
-  connection, it is recommended that it return a new Protocol object
-  each time it is called.
-
-  Optional keyword arguments:
-
-  - ``family``, ``proto``, ``flags``: Address familty,
-    protcol, and miscellaneous flags to be passed through
-    to ``getaddrinfo()``.  These all default to ``0``.
-    (The socket type is always ``SOCK_STREAM``.)
-
-  TBD: Support SSL?  I don't even know how to do that synchronously,
-  and I suppose it needs a certificate.
-
-  TBD: Maybe make the Future's result an object that can be used to
-  control the serving loop, e.g. to stop serving, abort all active
-  connections, and (if supported) adjust the backlog or other
-  parameters?  It could also have an API to inquire about active
-  connections.  Alternatively, return a Future (subclass?) that only
-  completes if the loop stops serving due to an error, or if it cannot
-  be started?  Cancelling it might stop the loop.
-
-TBD: Some platforms may not be interested in implementing all of
-these, e.g. start_serving() may be of no interest to mobile apps.
-(Although, there's a Minecraft server on my iPad...)
-
-The following methods for registering callbacks for file descriptors
-are optional.  If they are not implemented, accessing the method
-(without calling it) returns AttributeError.  The default
-implementation provides them but the user normally doesn't use these
-directly -- they are used by the transport implementations
-exclusively.  Also, on Windows these may be present or not depending
-on whether a select-based or IOCP-based event loop is used.  These
-take integer file descriptors only, not objects with a fileno()
-method.  The file descriptor should represent something pollable --
-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 ``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 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 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 ``Handle``
-  object.)  Returns ``True`` if a callback was removed, ``False``
-  if not.
-
-- ``remove_writer(fd)``.  This is to ``add_writer()`` as
-  ``remove_reader()`` is to ``add_reader()``.
-
-TBD: What about multiple callbacks per fd?  The current semantics is
-that ``add_reader()/add_writer()`` replace a previously registered
-callback.  Change this to raise an exception if a callback is already
-registered.
-
-The following methods for doing async I/O on sockets are optional.
-They are alternative to the previous set of optional methods, intended
-for transport implementations on Windows using IOCP (if the event loop
-supports it).  The socket argument has to be a non-blocking socket.
-
-- ``sock_recv(sock, n)``.  Receive up to ``n`` bytes from socket
-  ``sock``.  Returns a Future whose result on success will be a
-  bytes object on success.
-
-- ``sock_sendall(sock, data)``.  Send bytes ``data`` to the socket
-  ``sock``.  Returns a Future whose result on success will be
-  ``None``.  (TBD: Is it better to emulate ``sendall()`` or ``send()``
-  semantics?  I think ``sendall()`` -- but perhaps it should still
-  be *named* ``send()``?)
-
-- ``sock_connect(sock, address)``.  Connect to the given address.
-  Returns a Future whose result on success will be ``None``.
-
-- ``sock_accept(sock)``.  Accept a connection from a socket.  The
-  socket must be in listening mode and bound to an address.  Returns a
-  Future whose result on success will be a tuple ``(conn, peer)``
-  where ``conn`` is a connected non-blocking socket and ``peer`` is
-  the peer address.  (TBD: People tell me that this style of API is
-  too slow for high-volume servers.  So there's also
-  ``start_serving()`` above.  Then do we still need this?)
-
-TBD: Optional methods are not so good.  Perhaps these should be
-required?  It may still depend on the platform which set is more
-efficient.  Another possibility: document these as "for transports
-only" and the rest as "for anyone".
+Note: If these methods are statically known to be unsupported, they
+may return ``NotImplementedError`` instead of ``RuntimeError``.
 
 Callback Sequencing
 -------------------
@@ -470,8 +722,8 @@
 When two callbacks are scheduled for the same time, they are run
 in the order in which they are registered.  For example::
 
-  ev.call_soon(foo)
-  ev.call_soon(bar)
+  loop.call_soon(foo)
+  loop.call_soon(bar)
 
 guarantees that ``foo()`` is called before ``bar()``.
 
@@ -612,7 +864,7 @@
   ``exception()`` is called on a Future that is cancelled.
 
 - ``TimeoutError``.  An alias for ``concurrent.futures.TimeoutError``.
-  May be raised by ``EventLoop.run_until_complete()``.
+  May be raised by ``run_until_complete()``.
 
 A Future is associated with the default event loop when it is created.
 (TBD: Optionally pass in an alternative event loop instance?)
@@ -645,11 +897,9 @@
 Transports
 ----------
 
-A transport is an abstraction on top of a socket or something similar
-(for example, a UNIX pipe or an SSL connection).  Transports are
-strongly influenced by Twisted and PEP 3153.  Users rarely implement
-or instantiate transports -- rather, event loops offer utility methods
-to set up transports.
+Transports and protocols are strongly influenced by Twisted and PEP
+3153.  Users rarely implement or instantiate transports -- rather,
+event loops offer utility methods to set up transports.
 
 Transports work in conjunction with protocols.  Protocols are
 typically written without knowing or caring about the exact type of
@@ -659,6 +909,29 @@
 plain socket transport can be used with many different protocols
 besides HTTP (e.g. SMTP, IMAP, POP, FTP, IRC, SPDY).
 
+The most common type of transport is a bidirectional stream transport.
+There are also unidirectional stream transports (used for pipes) and
+datagram transports (used by the ``create_datagram_endpoint()``
+method).
+
+Methods For All Transports
+''''''''''''''''''''''''''
+
+- ``get_extra_info(name, default=None)``.  This is a catch-all method
+  that returns implementation-specific information about a transport.
+  The first argument is the name of the extra field to be retrieved.
+  The optional second argument is a default value to be returned.
+  Consult the implementation documentation to find out the supported
+  extra field names.  For an unsupported name, the default is always
+  returned.
+
+Bidirectional Stream Transports
+'''''''''''''''''''''''''''''''
+
+A bidrectional stream transport is an abstraction on top of a socket
+or something similar (for example, a pair of UNIX pipes or an SSL
+connection).
+
 Most connections have an asymmetric nature: the client and server
 usually have very different roles and behaviors.  Hence, the interface
 between transport and protocol is also asymmetric.  From the
@@ -669,7 +942,12 @@
 other data source), the transport calls the protocol's
 ``data_received()`` method.
 
-Transports have the following public methods:
+Nevertheless, the interface between transport and protocol used by
+bidirectional streams is the same for clients as it is for servers,
+since the connection between a client and a server is essentially a
+pair of streams, one in each direction.
+
+Bidirectional stream transports have the following public methods:
 
 - ``write(data)``.  Write some bytes.  The argument must be a bytes
   object.  Returns ``None``.  The transport is free to buffer the
@@ -741,9 +1019,58 @@
 protocol doesn't support flow control.  (Perhaps different names
 to avoid confusion between protocols and transports?)
 
+Unidirectional Stream Transports
+''''''''''''''''''''''''''''''''
+
+A writing stream transport supports the ``write()``, ``writelines()``,
+``write_eof()``, ``can_write_eof()``, close() and ``abort()`` methods
+described for bidrectional stream transports.
+
+A reading stream transport supports the ``pause()``, ``resume()`` and
+``close()`` methods described for bidrectional stream transports.
+
+A writing stream transport calls only ``connection_made()`` and
+``connection_lost()`` on its associated protocol.
+
+A reading stream transport can call all protocol methods specified in
+the Protocols section below (i.e., the previous two plus
+``data_received()`` and ``eof_received()``).
+
+Datagram Transports
+'''''''''''''''''''
+
+Datagram transports have these methods:
+
+- ``sendto(data, addr=None)``.  Sends a datagram (a bytes object).
+  The optional second argument is the destination address.  If
+  omitted, ``remote_addr`` must have been specified in the
+  ``create_datagram_endpoint()`` call that created this transport.  If
+  present, and ``remote_addr`` was specified, they must match.
+
+- ``abort()``.  Immediately close the transport.  Buffered data will
+  be discarded.
+
+- ``close()``.  Close the transport.  Buffered data will be
+  transmitted asynchronously.
+
+Datagram transports call the following methods on the associated
+protocol object: ``connection_made()``, ``connection_lost()``,
+``connection_refused()``, and ``datagram_received()``.  ("Connection"
+in these method names is a slight misnomer, but the concepts still
+exist: ``connection_made()`` means the transport representing the
+endpoint has been created, and ``connection_lost()`` means the
+transport is closed.  The ``connection_refused()`` method is called
+before ``connection_lost()`` when ``remote_addr`` was given and an
+explicit negative acknowledgement was received (this is a UDP
+feature).  (TBD: Do we need the latter?  It seems easy enough to
+implement this in the protocol if it needs to make the distinction.)
+
 Protocols
 ---------
 
+TBD Describe different kinds of protocols (bidrectional stream,
+unidirectional stream, datagram).
+
 Protocols are always used in conjunction with transports.  While a few
 common protocols are provided (e.g. decent though not necessarily
 excellent HTTP client and server implementations), most protocols will
@@ -796,9 +1123,10 @@
 
 Most interfaces taking a callback also take positional arguments.  For
 instance, to arrange for ``foo("abc", 42)`` to be called soon, you
-call ``ev.call_soon(foo, "abc", 42)``.  To schedule the call
-``foo()``, use ``ev.call_soon(foo)``.  This convention greatly reduces
-the number of small lambdas required in typical callback programming.
+call ``loop.call_soon(foo, "abc", 42)``.  To schedule the call
+``foo()``, use ``loop.call_soon(foo)``.  This convention greatly
+reduces the number of small lambdas required in typical callback
+programming.
 
 This convention specifically does *not* support keyword arguments.
 Keyword arguments are used to pass optional extra information about
@@ -808,7 +1136,7 @@
 keyword argument, you can use a lambda or ``functools.partial``.  For
 example::
 
-  ev.call_soon(functools.partial(foo, "abc", repeat=42))
+  loop.call_soon(functools.partial(foo, "abc", repeat=42))
 
 Choosing an Event Loop Implementation
 -------------------------------------
@@ -1032,7 +1360,7 @@
 - We may need APIs to control various timeouts.  E.g. we may want to
   limit the time spent in DNS resolution, connecting, ssl handshake,
   idle connection, close/shutdown, even per session.  Possibly it's
-  sufficient to add ``timeout`` keyword parameters to some methods,
+  sufficient to add ``timeout`` keyword arguments to some methods,
   and other timeouts can probably be implemented by clever use of
   ``call_later()`` and ``Task.cancel()``.  But it's possible that some
   operations need default timeouts, and we may want to change the
@@ -1048,8 +1376,10 @@
 References
 ==========
 
-- PEP 380 describes the semantics of ``yield from``.  TBD: Greg
-  Ewing's tutorial.
+- PEP 380 describes the semantics of ``yield from``.  
+
+- Greg Ewing's ``yield from`` tutorials:
+  http://www.cosc.canterbury.ac.nz/greg.ewing/python/yield-from/yield_from.html
 
 - PEP 3148 describes ``concurrent.futures.Future``.
 

-- 
Repository URL: http://hg.python.org/peps


More information about the Python-checkins mailing list