GeezerVison is showing github.com — bpo-31861: Add operator.aiter and operator.anext by jab · Pull Request #8895 · python/cpython
bpo-31861: Add operator.aiter and operator.anext
#8895
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement . We’ll occasionally send you account related emails.
Already on GitHub?
Sign in
to your account
Uh oh!
There was an error while loading. Please reload this page.
Show file tree
Hide file tree
Changes from all commits
Commits
File filter
Filter by extension
.py
(2)
.rst
(1)
All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Hide whitespace
Diff view
Hide whitespace
There are no files selected for viewing
Original file line number
Diff line number
Diff line change
Expand Up
@@ -10,14 +10,15 @@
This is the pure Python implementation of the module.
"""
__all__ = ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf',
'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand',
'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul',
'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift',
'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le',
'length_hint', 'lshift', 'lt', 'matmul', 'methodcaller', 'mod',
'mul', 'ne', 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift',
'setitem', 'sub', 'truediv', 'truth', 'xor']
__all__ = [
'abs', 'add', 'aiter', 'anext', 'and_', 'attrgetter', 'concat', 'contains',
'countOf', 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd',
'iand', 'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul',
'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_',
'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le', 'length_hint',
'lshift', 'lt', 'matmul', 'methodcaller', 'mod', 'mul', 'ne', 'neg', 'not_',
'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub', 'truediv', 'truth', 'xor',
]
from builtins import abs as _abs
Expand Down
Expand Up
@@ -404,6 +405,59 @@ def ixor(a, b):
return a
# Asynchronous Iterator Operations ********************************************#
_NOT_PROVIDED = object() # sentinel object to detect when a kwarg was not given
def aiter(obj, sentinel=_NOT_PROVIDED):
"""aiter(async_iterable) -> async_iterator
aiter(async_callable, sentinel) -> async_iterator
Like the iter() builtin but for async iterables and callables.
"""
from collections.abc import AsyncIterable, AsyncIterator
if sentinel is _NOT_PROVIDED:
if not isinstance(obj, AsyncIterable):
raise TypeError(f'aiter expected an AsyncIterable, got {type(obj)}')
ait = type(obj).__aiter__(obj)
if not isinstance(ait, AsyncIterator):
raise TypeError(f'obj.__aiter__() returned non-AsyncIterator: {type(ait)}')
return ait
if not callable(obj):
raise TypeError(f'aiter expected an async callable, got {type(obj)}')
async def ait():
while True:
value = await obj()
if value == sentinel:
break
yield value
return ait()
async def anext(async_iterator, default=_NOT_PROVIDED):
"""anext(async_iterator[, default])
Return the next item from the async iterator.
If default is given and the iterator is exhausted,
it is returned instead of raising StopAsyncIteration.
"""
from collections.abc import AsyncIterator
if not isinstance(async_iterator, AsyncIterator):
raise TypeError(f'anext expected an AsyncIterator, got {type(async_iterator)}')
anxt = type(async_iterator).__anext__
try:
return await anxt(async_iterator)
except StopAsyncIteration:
if default is _NOT_PROVIDED:
raise
return default
try:
from _operator import *
except ImportError:
Expand Down
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,5 @@
import inspect
import operator
import types
import unittest
Expand Down
Expand Up
@@ -372,6 +373,86 @@ def tearDown(self):
self.loop = None
asyncio.set_event_loop_policy(None)
def test_async_gen_operator_anext(self):
async def gen():
yield 1
yield 2
g = gen()
async def consume():
results = []
results.append(await operator.anext(g))
results.append(await operator.anext(g))
results.append(await operator.anext(g, 'buckle my shoe'))
return results
res = self.loop.run_until_complete(consume())
self.assertEqual(res, [1, 2, 'buckle my shoe'])
with self.assertRaises(StopAsyncIteration):
self.loop.run_until_complete(consume())
def test_async_gen_operator_aiter(self):
async def gen():
yield 1
yield 2
g = gen()
async def consume():
return [i async for i in operator.aiter(g)]
res = self.loop.run_until_complete(consume())
self.assertEqual(res, [1, 2])
def test_async_gen_operator_aiter_class(self):
results = []
loop = self.loop
class Gen:
async def __aiter__(self):
Comment thread
jab marked this conversation as resolved.
Resolved
Uh oh!
There was an error while loading. Please reload this page.
yield 1
await asyncio.sleep(0.01)
yield 2
g = Gen()
async def consume():
ait = operator.aiter(g)
while True:
try:
results.append(await operator.anext(ait))
except StopAsyncIteration:
break
self.loop.run_until_complete(consume())
self.assertEqual(results, [1, 2])
def test_async_gen_operator_aiter_2_arg(self):
async def gen():
yield 1
yield 2
yield None
g = gen()
async def foo():
return await operator.anext(g)
async def consume():
return [i async for i in operator.aiter(foo, None)]
res = self.loop.run_until_complete(consume())
self.assertEqual(res, [1, 2])
def test_operator_anext_bad_args(self):
self._test_bad_args(operator.anext)
def test_operator_aiter_bad_args(self):
self._test_bad_args(operator.aiter)
def _test_bad_args(self, afn):
async def gen():
yield 1
async def call_with_no_args():
await afn()
async def call_with_3_args():
await afn(gen(), 1, 2)
async def call_with_bad_args():
await afn(1, gen())
with self.assertRaises(TypeError):
self.loop.run_until_complete(call_with_no_args())
with self.assertRaises(TypeError):
self.loop.run_until_complete(call_with_3_args())
with self.assertRaises(TypeError):
self.loop.run_until_complete(call_with_bad_args())
async def to_list(self, gen):
res = []
async for i in gen:
Expand Down
Original file line number
Diff line number
Diff line change
@@ -0,0 +1 @@
Add the operator.aiter and operator.anext functions. Patch by Josh Bronson.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.