Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is the return type of __aenter__ and __aexit__?

Tags:

python-3.x

I have the following class

class MyService:
    def __init__(self, my_api: MyAPI) -> None:
        self._my_api = my_api
        self._session_usage_count = 0

    async def __aenter__(self) -> "MyService":
        if not self._session_usage_count:
            await self._my_api.init() # returns None
        self._session_usage_count += 1
        return self

    async def __aexit__(self, *exc: Any) -> None:
        self._session_usage_count -= 1
        if not self._session_usage_count:
            await self._my_api.close() # returns None

According to the doc both __aenter__ and __aexit__ must return an awaitable, but in my case it's not. Also I read that __aexit__ returns bool implicitly. So what should be the return type in my case?

like image 918
Ivan Petrushenko Avatar asked Jan 21 '26 17:01

Ivan Petrushenko


1 Answers

TLDR: Annotating an async def statement with just the return type of awaiting it is fine.


An async def statement creates a callable that always returns an awaitable. Repeating this in the return type is thus not necessary.

from typing import Callable, Awaitable

async def greetings() -> str:
    return "Hello World!"

c: Callable[..., Awaitable[str]] = greetings  # mypy: Success: no issues found in 1 source file

This allows to use the same return type annotation for an async protocol as one would use for the corresponding sync protocol – e.g. __exit__/__aexit__ in this case.

The (async) Context Manager Protocol specifically only says that __exit__/__aexit__ "should return a true value" if "the method wishes to suppress the exception". It is perfectly fine to return another value/type, including None, if the exception should not be suppressed.

like image 116
MisterMiyagi Avatar answered Jan 23 '26 16:01

MisterMiyagi