Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 711d25d

Browse files
committed
Merge 3.5 (issue #27243)
2 parents 5dee655 + a6f6edb commit 711d25d

13 files changed

Lines changed: 292 additions & 32 deletions

File tree

Doc/glossary.rst

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,12 @@ Glossary
7676

7777
asynchronous iterable
7878
An object, that can be used in an :keyword:`async for` statement.
79-
Must return an :term:`awaitable` from its :meth:`__aiter__` method,
80-
which should in turn be resolved in an :term:`asynchronous iterator`
81-
object. Introduced by :pep:`492`.
79+
Must return an :term:`asyncronous iterator` from its
80+
:meth:`__aiter__` method. Introduced by :pep:`492`.
8281

8382
asynchronous iterator
8483
An object that implements :meth:`__aiter__` and :meth:`__anext__`
85-
methods, that must return :term:`awaitable` objects.
84+
methods. ``__anext__`` must return an :term:`awaitable` object.
8685
:keyword:`async for` resolves awaitable returned from asynchronous
8786
iterator's :meth:`__anext__` method until it raises
8887
:exc:`StopAsyncIteration` exception. Introduced by :pep:`492`.

Doc/reference/compound_stmts.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ The following code::
726726
Is semantically equivalent to::
727727

728728
iter = (ITER)
729-
iter = await type(iter).__aiter__(iter)
729+
iter = type(iter).__aiter__(iter)
730730
running = True
731731
while running:
732732
try:

Doc/reference/datamodel.rst

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,6 +2360,7 @@ generators, coroutines do not directly support iteration.
23602360
Coroutine objects are automatically closed using the above process when
23612361
they are about to be destroyed.
23622362

2363+
.. _async-iterators:
23632364

23642365
Asynchronous Iterators
23652366
----------------------
@@ -2372,7 +2373,7 @@ Asynchronous iterators can be used in an :keyword:`async for` statement.
23722373

23732374
.. method:: object.__aiter__(self)
23742375

2375-
Must return an *awaitable* resulting in an *asynchronous iterator* object.
2376+
Must return an *asynchronous iterator* object.
23762377

23772378
.. method:: object.__anext__(self)
23782379

@@ -2385,7 +2386,7 @@ An example of an asynchronous iterable object::
23852386
async def readline(self):
23862387
...
23872388

2388-
async def __aiter__(self):
2389+
def __aiter__(self):
23892390
return self
23902391

23912392
async def __anext__(self):
@@ -2396,6 +2397,49 @@ An example of an asynchronous iterable object::
23962397

23972398
.. versionadded:: 3.5
23982399

2400+
.. note::
2401+
2402+
.. versionchanged:: 3.5.2
2403+
Starting with CPython 3.5.2, ``__aiter__`` can directly return
2404+
:term:`asynchronous iterators <asynchronous iterator>`. Returning
2405+
an :term:`awaitable` object will result in a
2406+
:exc:`PendingDeprecationWarning`.
2407+
2408+
The recommended way of writing backwards compatible code in
2409+
CPython 3.5.x is to continue returning awaitables from
2410+
``__aiter__``. If you want to avoid the PendingDeprecationWarning
2411+
and keep the code backwards compatible, the following decorator
2412+
can be used::
2413+
2414+
import functools
2415+
import sys
2416+
2417+
if sys.version_info < (3, 5, 2):
2418+
def aiter_compat(func):
2419+
@functools.wraps(func)
2420+
async def wrapper(self):
2421+
return func(self)
2422+
return wrapper
2423+
else:
2424+
def aiter_compat(func):
2425+
return func
2426+
2427+
Example::
2428+
2429+
class AsyncIterator:
2430+
2431+
@aiter_compat
2432+
def __aiter__(self):
2433+
return self
2434+
2435+
async def __anext__(self):
2436+
...
2437+
2438+
Starting with CPython 3.6, the :exc:`PendingDeprecationWarning`
2439+
will be replaced with the :exc:`DeprecationWarning`.
2440+
In CPython 3.7, returning an awaitable from ``__aiter__`` will
2441+
result in a :exc:`RuntimeError`.
2442+
23992443

24002444
Asynchronous Context Managers
24012445
-----------------------------

Doc/whatsnew/3.5.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,19 @@ be used inside a coroutine function declared with :keyword:`async def`.
247247
Coroutine functions are intended to be run inside a compatible event loop,
248248
such as the :ref:`asyncio loop <asyncio-event-loop>`.
249249

250+
251+
.. note::
252+
253+
.. versionchanged:: 3.5.2
254+
Starting with CPython 3.5.2, ``__aiter__`` can directly return
255+
:term:`asynchronous iterators <asynchronous iterator>`. Returning
256+
an :term:`awaitable` object will result in a
257+
:exc:`PendingDeprecationWarning`.
258+
259+
See more details in the :ref:`async-iterators` documentation
260+
section.
261+
262+
250263
.. seealso::
251264

252265
:pep:`492` -- Coroutines with async and await syntax

Include/genobject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ typedef struct {
5454
PyAPI_DATA(PyTypeObject) PyCoro_Type;
5555
PyAPI_DATA(PyTypeObject) _PyCoroWrapper_Type;
5656

57+
PyAPI_DATA(PyTypeObject) _PyAIterWrapper_Type;
58+
PyObject *_PyAIterWrapper_New(PyObject *aiter);
59+
5760
#define PyCoro_CheckExact(op) (Py_TYPE(op) == &PyCoro_Type)
5861
PyObject *_PyCoro_GetAwaitableIter(PyObject *o);
5962
PyAPI_FUNC(PyObject *) PyCoro_New(struct _frame *,

Lib/_collections_abc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class AsyncIterable(metaclass=ABCMeta):
156156
__slots__ = ()
157157

158158
@abstractmethod
159-
async def __aiter__(self):
159+
def __aiter__(self):
160160
return AsyncIterator()
161161

162162
@classmethod
@@ -176,7 +176,7 @@ async def __anext__(self):
176176
"""Return the next item or raise StopAsyncIteration when exhausted."""
177177
raise StopAsyncIteration
178178

179-
async def __aiter__(self):
179+
def __aiter__(self):
180180
return self
181181

182182
@classmethod

Lib/asyncio/compat.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
PY34 = sys.version_info >= (3, 4)
66
PY35 = sys.version_info >= (3, 5)
7+
PY352 = sys.version_info >= (3, 5, 2)
78

89

910
def flatten_list_bytes(list_of_data):

Lib/asyncio/streams.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,3 +689,9 @@ def __anext__(self):
689689
if val == b'':
690690
raise StopAsyncIteration
691691
return val
692+
693+
if compat.PY352:
694+
# In Python 3.5.2 and greater, __aiter__ should return
695+
# the asynchronous iterator directly.
696+
def __aiter__(self):
697+
return self

Lib/test/test_coroutines.py

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,8 +1255,9 @@ async def __anext__(self):
12551255

12561256
buffer = []
12571257
async def test1():
1258-
async for i1, i2 in AsyncIter():
1259-
buffer.append(i1 + i2)
1258+
with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"):
1259+
async for i1, i2 in AsyncIter():
1260+
buffer.append(i1 + i2)
12601261

12611262
yielded, _ = run_async(test1())
12621263
# Make sure that __aiter__ was called only once
@@ -1268,12 +1269,13 @@ async def test1():
12681269
buffer = []
12691270
async def test2():
12701271
nonlocal buffer
1271-
async for i in AsyncIter():
1272-
buffer.append(i[0])
1273-
if i[0] == 20:
1274-
break
1275-
else:
1276-
buffer.append('what?')
1272+
with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"):
1273+
async for i in AsyncIter():
1274+
buffer.append(i[0])
1275+
if i[0] == 20:
1276+
break
1277+
else:
1278+
buffer.append('what?')
12771279
buffer.append('end')
12781280

12791281
yielded, _ = run_async(test2())
@@ -1286,12 +1288,13 @@ async def test2():
12861288
buffer = []
12871289
async def test3():
12881290
nonlocal buffer
1289-
async for i in AsyncIter():
1290-
if i[0] > 20:
1291-
continue
1292-
buffer.append(i[0])
1293-
else:
1294-
buffer.append('what?')
1291+
with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"):
1292+
async for i in AsyncIter():
1293+
if i[0] > 20:
1294+
continue
1295+
buffer.append(i[0])
1296+
else:
1297+
buffer.append('what?')
12951298
buffer.append('end')
12961299

12971300
yielded, _ = run_async(test3())
@@ -1338,7 +1341,7 @@ async def foo():
13381341

13391342
def test_for_4(self):
13401343
class I:
1341-
async def __aiter__(self):
1344+
def __aiter__(self):
13421345
return self
13431346

13441347
def __anext__(self):
@@ -1368,8 +1371,9 @@ def __anext__(self):
13681371
return 123
13691372

13701373
async def foo():
1371-
async for i in I():
1372-
print('never going to happen')
1374+
with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"):
1375+
async for i in I():
1376+
print('never going to happen')
13731377

13741378
with self.assertRaisesRegex(
13751379
TypeError,
@@ -1393,7 +1397,7 @@ class Iterable:
13931397
def __init__(self):
13941398
self.i = 0
13951399

1396-
async def __aiter__(self):
1400+
def __aiter__(self):
13971401
return self
13981402

13991403
async def __anext__(self):
@@ -1417,7 +1421,11 @@ async def main():
14171421
I += 1
14181422
I += 1000
14191423

1420-
run_async(main())
1424+
with warnings.catch_warnings():
1425+
warnings.simplefilter("error")
1426+
# Test that __aiter__ that returns an asyncronous iterator
1427+
# directly does not throw any warnings.
1428+
run_async(main())
14211429
self.assertEqual(I, 111011)
14221430

14231431
self.assertEqual(sys.getrefcount(manager), mrefs_before)
@@ -1470,15 +1478,65 @@ def test_for_7(self):
14701478
class AI:
14711479
async def __aiter__(self):
14721480
1/0
1481+
async def foo():
1482+
nonlocal CNT
1483+
with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"):
1484+
async for i in AI():
1485+
CNT += 1
1486+
CNT += 10
1487+
with self.assertRaises(ZeroDivisionError):
1488+
run_async(foo())
1489+
self.assertEqual(CNT, 0)
1490+
1491+
def test_for_8(self):
1492+
CNT = 0
1493+
class AI:
1494+
def __aiter__(self):
1495+
1/0
14731496
async def foo():
14741497
nonlocal CNT
14751498
async for i in AI():
14761499
CNT += 1
14771500
CNT += 10
14781501
with self.assertRaises(ZeroDivisionError):
1479-
run_async(foo())
1502+
with warnings.catch_warnings():
1503+
warnings.simplefilter("error")
1504+
# Test that if __aiter__ raises an exception it propagates
1505+
# without any kind of warning.
1506+
run_async(foo())
14801507
self.assertEqual(CNT, 0)
14811508

1509+
def test_for_9(self):
1510+
# Test that PendingDeprecationWarning can safely be converted into
1511+
# an exception (__aiter__ should not have a chance to raise
1512+
# a ZeroDivisionError.)
1513+
class AI:
1514+
async def __aiter__(self):
1515+
1/0
1516+
async def foo():
1517+
async for i in AI():
1518+
pass
1519+
1520+
with self.assertRaises(PendingDeprecationWarning):
1521+
with warnings.catch_warnings():
1522+
warnings.simplefilter("error")
1523+
run_async(foo())
1524+
1525+
def test_for_10(self):
1526+
# Test that PendingDeprecationWarning can safely be converted into
1527+
# an exception.
1528+
class AI:
1529+
async def __aiter__(self):
1530+
pass
1531+
async def foo():
1532+
async for i in AI():
1533+
pass
1534+
1535+
with self.assertRaises(PendingDeprecationWarning):
1536+
with warnings.catch_warnings():
1537+
warnings.simplefilter("error")
1538+
run_async(foo())
1539+
14821540
def test_copy(self):
14831541
async def func(): pass
14841542
coro = func()

Lib/test/test_grammar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ def test_async_for(self):
11141114
class Done(Exception): pass
11151115

11161116
class AIter:
1117-
async def __aiter__(self):
1117+
def __aiter__(self):
11181118
return self
11191119
async def __anext__(self):
11201120
raise StopAsyncIteration

0 commit comments

Comments
 (0)