question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Type error when using contextlib.asynccontextmanager

See original GitHub issue

I have 2 Python file foo.py and test_foo.py. foo.py:

from contextlib import asynccontextmanager
from typing import AsyncGenerator
class Foo:
    async def bar()->int:
        print("Calling func bar")
        return 1
    async def __aenter__(self):
        return self
    async def __aexit__(self, exc_type, exc, tb):
        return

@asynccontextmanager
async def get_cm() -> AsyncGenerator[Foo, None]:
        yield Foo()

async def call_bar()->int:
    async with get_cm() as foo:
        return await foo.bar()

test_foo.py:

from testslide.dsl import context
from foo import call_bar,Foo
from testslide import StrictMock

module = "foo"

@context
def foo_test(context):
    context.memoize(
        "foo_mock", lambda self: StrictMock(template=Foo, default_context_manager=True)
    )

    @context.before
    async def mock_get_cm(self):
        self.mock_callable(module, "get_cm").to_return_value(
            self.foo_mock
        )
        self.mock_async_callable(self.foo_mock, "bar").to_return_value(3)
    @context.example
    async def test_bar(self):
        self.assertEqual(await call_bar(),3)

When I ran testslide test_foo.py. I hit this type error below.

Failures:

  1) foo test: test bar
    1) TypeError: type of return must be collections.abc.AsyncGenerator; got testslide.strict_mock.FooStrictMock instead: <StrictMock 0x10612BF10 template=foo.Foo testslide_test.py:10>
    Defined at /Users/qxue/testslide_test.py:15
      File ".pyenv/versions/3.8.2/lib/python3.8/asyncio/runners.py", line 43, in run
        return loop.run_until_complete(main)
      File ".pyenv/versions/3.8.2/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
        return future.result()
      File "testslide_test.py", line 21, in test_bar
        self.assertEqual(await call_bar(),3)
      File "foo.py", line 20, in call_bar
        async with get_cm() as foo:

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
fornellascommented, May 13, 2020

@xueqiao001 thanks for taking the time to report this.

When you declare:

@asynccontextmanager
async def get_cm() -> AsyncGenerator[Foo, None]:
        yield Foo()

You’re annotating that the resulting function after the @asynccontextmanager decorator returns a type of AsyncGenerator[Foo, None]. This is not true:

In [3]: import foo                                                                                                            

In [4]: foo.get_cm()                                                                                                          
Out[4]: <contextlib._AsyncGeneratorContextManager at 0x7fd9488a4d30>

In [5]: type(foo.get_cm())                                                                                                    
Out[5]: contextlib._AsyncGeneratorContextManager

In [6]: type(foo.get_cm()).mro()                                                                                              
Out[6]: 
[contextlib._AsyncGeneratorContextManager,
 contextlib._GeneratorContextManagerBase,
 contextlib.AbstractAsyncContextManager,
 abc.ABC,
 object]

It is confusing, because this is legit:

async def get_cm() -> AsyncGenerator[Foo, None]:
        yield Foo()
In [17]: get_cm()                                                                                                             
Out[17]: <async_generator object get_cm at 0x7fd948a5ae50>

In [18]: type(get_cm())                                                                                                       
Out[18]: async_generator

In [19]: type(get_cm()).mro()                                                                                                 
Out[19]: [async_generator, object]

By changing get_cm to the correct typing annotation:

@asynccontextmanager
async def get_cm() -> AsyncContextManager:
        yield Foo()

This test goes green.

I hope this helped. If I missed something, please re-open this issue.

0reactions
fornellascommented, Jul 7, 2020

Python Upstream bug filed: https://bugs.python.org/issue41231.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Type error when using contextlib.asynccontextmanager #193
I have 2 Python file foo.py and test_foo.py. foo.py: from contextlib import asynccontextmanager from typing import AsyncGenerator class Foo: ...
Read more >
contextlib — Utilities for with-statement contexts ... - Python Docs
Changed in version 3.11: Raises TypeError instead of AttributeError if cm is not an asynchronous context manager. push_async_exit(exit)¶. Similar to push() ...
Read more >
Type hint of AsyncContextManager in PyCharm - Stack Overflow
I have an async method I use to manage the session with my database in asynchronous environments @contextlib.asynccontextmanager async def ...
Read more >
PyCharm doesn't infer types when using contextlib ... - YouTrack
PY-29891 PyCharm doesn't infer AsyncContextManager types even using type ... error: Argument 1 to "contextmanager" has incompatible type "Callable[[], ...
Read more >
Python Asyncio Part 3 – Asynchronous Context Managers and ...
TYPING NOTE: If you are using the typing library then there is an abstract type class provided for asynchronous context managers AsyncContextManager[T] ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found