56

Suppose I have code like this

async def fetch_text() -> str: return "text " async def show_something(): something = await fetch_text() print(something) 

Which is fine. But then I want to clean the data, so I do

async def fetch_text() -> str: return "text " def fetch_clean_text(text: str) -> str: text = await fetch_text() return text.strip(text) async def show_something(): something = fetch_clean_text() print(something) 

(I could clean text inside show_something(), but let's assume that show_something() can print many things and doesn't or shouldn't know the proper way of cleaning them.)

This is of course a SyntaxError: 'await' outside async function. But—if this code could run—while the await expression is not placed inside a coroutine function, it is executed in the context of one. Why this behavior is not allowed?

I see one pro in this design; in my latter example, you can't see that show_something()'s body is doing something that can result in its suspension. But if I were to make fetch_clean_text() a coroutine, not only would it complicate things but would probably also reduce performance. It just makes little sense to have another coroutine that doesn't perform any I/O by itself. Is there a better way?

3
  • 3
    So, what actually prevents you from cleaning the text with a pure function after you have fetched it? Commented Aug 26, 2018 at 14:45
  • Shortly, a normal function cannot be scheduled but await needs schedule. As the above suggestted, you can use a pure function after fetching. Commented Aug 26, 2018 at 15:12
  • 2
    @taras this is just an example code. suppose I'm writing a library that fetches titles for URLs, but handles special cases such as yutube's URLs separately, fetching stuff using the API. So now I have my_library.fetch_title(url) that makes a lot of internal calls that eventually lead to some IO. Now if I was to make it also async-compatible, I'd (probably?) have to duplicate half of my code, adding async and await stuff to methods, and in most cases doing so would probably make little sense. Commented Aug 26, 2018 at 15:14

2 Answers 2

50

You can only use await in an async environment. Try changing the sync function to async:

import asyncio whatever = . . . async def function(param) -> asyncio.coroutine: await param asyncio.run(function(whatever)) 

Simple and easy.

Sign up to request clarification or add additional context in comments.

1 Comment

What if the async method is part of a class?
22

I see one pro in this design; in my latter example, you can't see that show_something()'s body is doing something that can result in its suspension.

That's exactly why it designed this way. Writing concurrent code can be very tricky and asyncio authors decided that it's critically important to always explicitly mark places of suspend in code.

This article explains it in details (you can start from "Get To The Point Already" paragraph).

But if I were to make fetch_clean_text() a coroutine, not only would it complicate things but would probably also reduce performance.

You need coroutines almost exclusively when you deal with I/O. I/O always takes much-much more time than overhead for using coroutines. So I guess it can be said - no, comparing to I/O you already deal with, you won't lose any significant amount of execution time for using coroutines.

Is there a better way?

Only way I can suggest: is to maximally split logic that deals with I/O (async part) from rest of the code (sync part).

from typing import Awaitable def clean_text(text: str) -> str: return text.strip(text) async def fetch_text() -> Awaitable[str]: return "text " async def fetch_clean_text(text: str) -> Awaitable[str]: text = await fetch_text() return clean_text(text) async def show_something(): something = await fetch_clean_text() print(something) 

3 Comments

How do we start (and await ?) this whole flow?
@javadba you should run an event loop that will start to execute coroutines. The Easiest way to do it is to call asyncio.run.
@mod can someone pls empty the edit queue for this question? i'm guessing a trillion ppl would like Mikhail Gerasimov's question to be worked into the answer - at least that's what i was trying to do.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.