Skip to content

Behaviour of AsyncExitStack in strict mode #12226

Closed

Description

@michaeloliverx

Bug Report

To Reproduce

from contextlib import AsyncExitStack

import httpx


async def main() -> None:
    async with AsyncExitStack() as stack:
        http_client = await stack.enter_async_context(httpx.AsyncClient())
        reveal_type(http_client)

Output with strict = false:

repro.py:9: note: Revealed type is "httpx._client.AsyncClient*"

Output with strict = true:

repro.py:8: error: Need type annotation for "http_client"  [var-annotated]
repro.py:8: error: Argument 1 to "enter_async_context" of "AsyncExitStack" has incompatible type "AsyncClient"; expected "AsyncContextManager[<nothing>]"  [arg-type]
repro.py:9: note: Revealed type is "Any"
Found 2 errors in 1 file (checked 1 source file)

Expected Behavior

I would expect the type of http_client to not need annotated and to be inferred from the return type of enter_async_context. Is this behaviour correct?

Your Environment

  • Mypy version used: 0.931
  • Mypy configuration options from mypy.ini (and other config files):
[tool.mypy]
strict = true
show_error_codes = true
  • Python version used: 3.9.0
  • Operating system and version: Windows 10

Activity

  1. JelleZijlstra commented on Feb 21, 2022

    @JelleZijlstra
    Member

    The problem is in the annotations for the httpx library: it has declared overly permissive types for the AsyncClient.__aexit__ method. If you change the code mypy tells you more precisely:

    % python -m mypy --strict samples/asynccli.py
    samples/asynccli.py:4: error: Incompatible types in assignment (expression has type "AsyncClient", variable has type "AsyncContextManager[Any]")
    samples/asynccli.py:4: note: Following member(s) of "AsyncClient" have conflicts:
    samples/asynccli.py:4: note:     Expected:
    samples/asynccli.py:4: note:         def __aexit__(self, Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]) -> Awaitable[Optional[bool]]
    samples/asynccli.py:4: note:     Got:
    samples/asynccli.py:4: note:         def __aexit__(self, exc_type: Type[BaseException] = ..., exc_value: BaseException = ..., traceback: TracebackType = ...) -> Coroutine[Any, Any, None]
    Found 1 error in 1 file (checked 1 source file)
    % cat samples/asynccli.py 
    import httpx
    from typing import Any, AsyncContextManager
    
    x: AsyncContextManager[Any] = httpx.AsyncClient()
    

    The annotations at https://github.com/encode/httpx/blob/7883c98556eaa7630454d5b62426a5c497542408/httpx/_client.py#L1970 need to be changed to Optional.

  2. michaeloliverx commented on Feb 21, 2022

    @michaeloliverx
    Author

    Thank you, I will create a report in the httpx repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions