From e6f60d8dce6e41c3a8dd42795e52192a4fbab58c Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 9 Jun 2016 17:57:41 -0400 Subject: [PATCH] PEP 492: Update __aiter__ protocol --- pep-0492.txt | 113 ++++++++++++++++++++++----------------------------- 1 file changed, 49 insertions(+), 64 deletions(-) diff --git a/pep-0492.txt b/pep-0492.txt index 2d5c98804..c84c4f86a 100644 --- a/pep-0492.txt +++ b/pep-0492.txt @@ -40,18 +40,36 @@ programming, as many other languages have adopted, or are planning to adopt, similar features: [2]_, [5]_, [6]_, [7]_, [8]_, [10]_. -API Design and Implementation Note -================================== +API Design and Implementation Revisions +======================================= -Feedback on the initial beta release of Python 3.5 resulted in a redesign -of the object model supporting this PEP to more clearly separate native -coroutines from generators - rather than being a new kind of generator, -native coroutines are now their own completely distinct type (implemented -in [17]_). +1. Feedback on the initial beta release of Python 3.5 resulted in a + redesign of the object model supporting this PEP to more clearly + separate native coroutines from generators - rather than being a + new kind of generator, native coroutines are now their own + completely distinct type (implemented in [17]_). -This change was implemented based primarily due to problems encountered -attempting to integrate support for native coroutines into the Tornado web -server (reported in [18]_). + This change was implemented based primarily due to problems + encountered attempting to integrate support for native coroutines + into the Tornado web server (reported in [18]_). + +2. In CPython 3.5.2, the ``__aiter__`` protocol was updated. + + Before 3.5.2, ``__aiter__`` was expected to return an *awaitable* + resolving to an *asynchronous iterator*. Starting with 3.5.2, + ``__aiter__`` should return asynchronous iterators directly. + + If the old protocol is used in 3.5.2, Python will raise a + ``PendingDeprecationWarning``. + + In CPython 3.6, the old ``__aiter__`` protocol will still be + supported with a ``DeprecationWarning`` being raised. + + In CPython 3.7, the old ``__aiter__`` protocol will no longer be + supported: a ``RuntimeError`` will be raised if ``__aiter__`` + returns anything but an asynchronous iterator. + + See [19]_ for more details. Rationale and Goals @@ -209,11 +227,6 @@ can be one of: Objects with ``__await__`` method are called *Future-like* objects in the rest of this PEP. - Also, please note that ``__aiter__`` method (see its definition - below) cannot be used for this purpose. It is a different protocol, - and would be like using ``__iter__`` instead of ``__call__`` for - regular callables. - It is a ``TypeError`` if ``__await__`` returns anything but an iterator. @@ -428,7 +441,7 @@ iteration: 1. An object must implement an ``__aiter__`` method (or, if defined with CPython C API, ``tp_as_async.am_aiter`` slot) returning an - *awaitable* resulting in an *asynchronous iterator object*. + *asynchronous iterator object*. 2. An *asynchronous iterator object* must implement an ``__anext__`` method (or, if defined with CPython C API, ``tp_as_async.am_anext`` @@ -440,7 +453,7 @@ iteration: An example of asynchronous iterable:: class AsyncIterable: - async def __aiter__(self): + def __aiter__(self): return self async def __anext__(self): @@ -468,7 +481,7 @@ proposed:: which is semantically equivalent to:: iter = (ITER) - iter = await type(iter).__aiter__(iter) + iter = type(iter).__aiter__(iter) running = True while running: try: @@ -510,7 +523,7 @@ The following code illustrates new asynchronous iteration protocol:: async def _prefetch(self): ... - async def __aiter__(self): + def __aiter__(self): return self async def __anext__(self): @@ -527,7 +540,7 @@ then the ``Cursor`` class can be used as follows:: which would be equivalent to the following code:: - i = await Cursor().__aiter__() + i = Cursor().__aiter__() while True: try: row = await i.__anext__() @@ -551,7 +564,7 @@ iterators. def __init__(self, obj): self._it = iter(obj) - async def __aiter__(self): + def __aiter__(self): return self async def __anext__(self): @@ -814,37 +827,6 @@ Asynchronous iterator `Asynchronous Iterators and "async for"`_ for details. -List of functions and methods -============================= - -================= =================================== ================= -Method Can contain Can't contain -================= =================================== ================= -async def func await, return value yield, yield from -async def __a*__ await, return value yield, yield from -def __a*__ return awaitable await -def __await__ yield, yield from, return iterable await -generator yield, yield from, return value await -================= =================================== ================= - -Where: - -* "async def func": native coroutine; - -* "async def __a*__": ``__aiter__``, ``__anext__``, ``__aenter__``, - ``__aexit__`` defined with the ``async`` keyword; - -* "def __a*__": ``__aiter__``, ``__anext__``, ``__aenter__``, - ``__aexit__`` defined without the ``async`` keyword, must return an - *awaitable*; - -* "def __await__": ``__await__`` method to implement *Future-like* - objects; - -* generator: a "regular" generator, function defined with ``def`` and - which contains a least one ``yield`` or ``yield from`` expression. - - Transition Plan =============== @@ -1080,19 +1062,20 @@ async/await, and because it makes working with many languages in one project easier (Python with ECMAScript 7 for instance). -Why "__aiter__" returns awaitable ---------------------------------- +Why "__aiter__" does not return an awaitable +-------------------------------------------- -In principle, ``__aiter__`` could be a regular function. There are -several good reasons to make it a coroutine: +PEP 492 was accepted in CPython 3.5.0 with ``__aiter__`` defined as +a method, that was expected to return an awaitable resolving to an +asynchronous iterator. -* as most of the ``__anext__``, ``__aenter__``, and ``__aexit__`` - methods are coroutines, users would often make a mistake defining it - as ``async`` anyways; +In 3.5.2 (as PEP 492 was accepted on a provisional basis) the +``__aiter__`` protocol was updated to return asynchronous iterators +directly. -* there might be a need to run some asynchronous operations in - ``__aiter__``, for instance to prepare DB queries or do some file - operation. +The motivation behind this change is to make it possible to +implement asynchronous generators in Python. See [19]_ for +more details. Importance of "async" keyword @@ -1171,8 +1154,8 @@ Why magic methods start with "a" New asynchronous magic methods ``__aiter__``, ``__anext__``, ``__aenter__``, and ``__aexit__`` all start with the same prefix "a". -An alternative proposal is to use "async" prefix, so that ``__aiter__`` -becomes ``__async_iter__``. However, to align new magic methods with +An alternative proposal is to use "async" prefix, so that ``__anext__`` +becomes ``__async_next__``. However, to align new magic methods with the existing ones, such as ``__radd__`` and ``__iadd__`` it was decided to use a shorter version. @@ -1454,6 +1437,8 @@ References .. [18] http://bugs.python.org/issue24400 +.. [19] http://bugs.python.org/issue27243 + Acknowledgments ===============