#๐ How do you transform an async generator into a coroutine?
140 messages ยท Page 1 of 1 (latest)
@granite frost
Remember to:
- Ask your Python question, not if you can ask or if there's an expert who can help.
- Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
- Explain what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
Wrap it in a coroutine that collects it
!pip asyncstdlib has some functions that may be useful.
Example of a function: ```py
async def alist(aiter: AsyncIterable[T]) -> list[T]:
return [item async for item in aiter]
it collects an async iterator (or generator) to a list
I think this worked:
from asyncstdlib import list as alist
async def async_generator_to_coroutine(async_generator):
return alist(async_generator)
tasks.append(async_generator_to_coroutine(item))
You need to await alist
i think tasks.append should handle that
no, it will make the task return a coroutine
i mean results = await asyncio.gather(*tasks)
Why not just append alist(item) directly?
i think i'm just realizing that now
how can you tell if something is a async_generator specifically
it's marked async and has yield
the type checker knows. hover over it or pass it to typing.reveal_type
You can also use inspect.isasyncgenerator(func)
!d inspect.isasyncgenfunction
inspect.isasyncgenfunction(object)```
Return `True` if the object is an [asynchronous generator](https://docs.python.org/3/glossary.html#term-asynchronous-generator) function, for example:
```py
>>> async def agen():
... yield 1
...
>>> inspect.isasyncgenfunction(agen)
True
``` Added in version 3.6.
Changed in version 3.8: Functions wrapped in [`functools.partial()`](https://docs.python.org/3/library/functools.html#functools.partial) now return `True` if the wrapped function is a [asynchronous generator](https://docs.python.org/3/glossary.html#term-asynchronous-generator) function.
this whole question seems odd; what is the actual use case?
yeah, using inspect is odd for adding tasks.
if you await a coroutine and get a coroutine back, you fucked up
Can you share all your code?
!paste
If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/
After pasting your code, save it by clicking the Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.
high level i have implemented some utility functions for mangaging concurrency by way of Promise.map_series, Promise.reduce_series and Promise.parallel
which lets me do stuff like:
tasks.append(asyncio.create_task(Promise.series([
Promise.parallel([
Promise.series([
lambda subproduct: fetch(session, "get", "/api/sub-product/" + subproduct["id"]),
lambda data: data.json()
], subproduct),
that looks more JS than Python
yes
I come from a very different school of thought in this matter; not sure I can help
well i think my core issue is fixed with alist
now I just need a condition for identifying a async_generator_asend
so instanceof? probably?
async_generator_asend
it's not something you can import
try the types module
Why do you need to check the type?
is that Promise type your code?
yes
to conditionally wrap the async_generator_asend in alist() if it's an async generator
Why would it not return an async generator?
instead of async_generator_asend you mean?
async def will_always_be_async_generator():
if False:
yield
That will always be a generator. It's just empty.
sorry to say but this looks to me like you're making this unnecessarily complicated
Can you share your code so we can get a better idea of what you're actually trying to do?
yes please
^
this doesn't yet answer the question of what you're actually trying to accomplish
just the lambdas
?
I see no generators in your code
comes from the .__anext__() hack to get async lambdas (https://stackoverflow.com/a/66330279)
I think you should reconsider what you're doing just for the DX
i'm thoroughly enjoying the dx ๐
i could not use lambdas and write actual functions and just pass their names in
calling __anext__() doesn't return a generator.
oh maybe the for _ in "_" bit then
yes, that creates a generator
the object you call anext on is a generator
specifically, it's async _ in ...
asyncstdlib has a function to convert a regular iterable into an async iterable
so i could get rid of the dumb hack?
i think i'll still have the problem of needing to use the await keyword within the lambdas which isn't allowed
but yeah to your point i could just not use lambdas
async def do_thing(session):
foos = await sdk.foo.get_all_foos(session)
for foo in foos:
bars = await sdk.bar.get_all_bars_by_foo_name(session, foo["name"])
for bar in bars:
results = await acsdk.bar.delete_bar_by_id(session, bar["id"])
print(results)
async def main():
session = init_session(api_key)
await do_thing(session)
you could even make this a list comprehension if you wanted
sure but now it's all in series
you never called parallel anyway
i do in other places in the code
The way you're calling parallel isn't how generators work
here you want the for foo in foos part to work in parallel, right?
i'm mostly just handling coroutines, i'm only using generators as a part of the async lambda hack
You'll probably want to replace inspect.isasyncgenfunction with inspect.isasyncgen
generator functions return generators
don't use __anext__
ah! using __aiter__ may have done it
You can also do hasattr(item, "__anext__") to check if something is an async iterator
I don't think I'm any closer to understanding what this enormous hack intends to accomplish
Also hasattr(item, "__aiter__")
python doesn't really seem to have a good answer to managing concurrency, that's the problem
async iterators are also async iterables
@granite frost that's a really broad topic; what solutions have you researched?
have you tried asyncio.gather() or asyncio.TaskGroup yet?
in short, what are you missing?
Try doing this. ```py
for item in iterable:
if inspect.isasyncgen(item):
tasks.append(alist(item))
else:
tasks.append(item)
yep that's what i have
btw lambdas can't be generators
your lambda with get_all_foos is actually returning tuple[Session, generator_send]
yeah what is a generator_send
I'm bugging out since I'm not getting any answers
I'm curious how you were able to call __anext__ on a non-async generator
what I've been asking is what you're doing these things for?
ok, simply awaiting something inside the generator will make it async
I think they're making a toy
toy?
for fun
eh
it's for interacting with an api, it's a whole bunch of fetch requests
Just use a normal async function
yeah
you're adding unneeded complexity
!pip aiolimit has some useful functions
im guessing you meant itertools
neat
cheers! thanks for the help!
This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.
๐ How do you transform an async generator into a coroutine?