ktosiek / pytest-vcr

Py.test integration with VCR.py
MIT License
150 stars 25 forks source link

'MockClientResponse.release' was never awaited #18

Closed JulienPalard closed 5 years ago

JulienPalard commented 5 years ago

I found a bug with pytest-aiohttp / pytest / aiohttp / pytest-vcr:

$ python3 -m venv /tmp/pytestcasettebug
$ source /tmp/pytestcasettebug/bin/activate
$ cat tests/test_mdk.py 
import aiohttp
import pytest

@pytest.mark.vcr
async def test_mdk():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://mdk.fr") as resp:
            assert resp.status == 200

$ pip install pytest aiohttp pytest-vcr pytest-aiohttp
$ pytest
=============================================================== test session starts ===============================================================
platform linux -- Python 3.7.2, pytest-4.1.1, py-1.7.0, pluggy-0.8.1
rootdir: /home/mdk/clones/meltygroup/vcrbug, inifile:
plugins: vcr-1.0.1, aiohttp-0.3.0
collected 1 item                                                                                                                                  

tests/test_mdk.py .                                                                                                                         [100%]

============================================================ 1 passed in 0.19 seconds =============================================================
$ pytest
=============================================================== test session starts ===============================================================
platform linux -- Python 3.7.2, pytest-4.1.1, py-1.7.0, pluggy-0.8.1
rootdir: /home/mdk/clones/meltygroup/vcrbug, inifile:
plugins: vcr-1.0.1, aiohttp-0.3.0
collected 1 item                                                                                                                                  

tests/test_mdk.py F                                                                                                                         [100%]

==================================================================== FAILURES =====================================================================
____________________________________________________________________ test_mdk _____________________________________________________________________

pyfuncitem = <Function test_mdk>

    def pytest_pyfunc_call(pyfuncitem):  # type: ignore
        """
        Run coroutines in an event loop instead of a normal function call.
        """
        fast = pyfuncitem.config.getoption("--aiohttp-fast")
        if asyncio.iscoroutinefunction(pyfuncitem.function):
            existing_loop = pyfuncitem.funcargs.get('loop', None)
            with _runtime_warning_context():
                with _passthrough_loop_context(existing_loop, fast=fast) as _loop:
                    testargs = {arg: pyfuncitem.funcargs[arg]
                                for arg in pyfuncitem._fixtureinfo.argnames}
>                   _loop.run_until_complete(pyfuncitem.obj(**testargs))

/tmp/pytestcasettebug/lib/python3.7/site-packages/aiohttp/pytest_plugin.py:169: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.7/contextlib.py:119: in __exit__
    next(self.gen)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    @contextlib.contextmanager
    def _runtime_warning_context():  # type: ignore
        """
        Context manager which checks for RuntimeWarnings, specifically to
        avoid "coroutine 'X' was never awaited" warnings being missed.

        If RuntimeWarnings occur in the context a RuntimeError is raised.
        """
        with warnings.catch_warnings(record=True) as _warnings:
            yield
            rw = ['{w.filename}:{w.lineno}:{w.message}'.format(w=w)
                  for w in _warnings  # type: ignore
                  if w.category == RuntimeWarning]
            if rw:
                raise RuntimeError('{} Runtime Warning{},\n{}'.format(
                    len(rw),
                    '' if len(rw) == 1 else 's',
>                   '\n'.join(rw)
                ))
E               RuntimeError: 1 Runtime Warning,
E               /tmp/pytestcasettebug/lib/python3.7/site-packages/aiohttp/client.py:1019:coroutine 'MockClientResponse.release' was never awaited

/tmp/pytestcasettebug/lib/python3.7/site-packages/aiohttp/pytest_plugin.py:130: RuntimeError
================================================================ warnings summary =================================================================
tests/test_mdk.py::test_mdk
  /tmp/pytestcasettebug/lib/python3.7/site-packages/yaml/constructor.py:126: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
    if not isinstance(key, collections.Hashable):

-- Docs: https://docs.pytest.org/en/latest/warnings.html
====================================================== 1 failed, 1 warnings in 0.06 seconds =======================================================

$ pip freeze
aiohttp==3.5.4
async-timeout==3.0.1
atomicwrites==1.2.1
attrs==18.2.0
chardet==3.0.4
idna==2.8
more-itertools==5.0.0
multidict==4.5.2
pkg-resources==0.0.0
pluggy==0.8.1
py==1.7.0
pytest==4.1.1
pytest-aiohttp==0.3.0
pytest-vcr==1.0.1
PyYAML==3.13
six==1.12.0
vcrpy==2.0.1
wrapt==1.11.0
yarl==1.3.0

I cannot reproduce this bug with vcrpy only, where I'm having a warning: RuntimeWarning: coroutine 'handle_coroutine' was never awaited.

ktosiek commented 5 years ago

I don't think it's a pytest-vcr problem: I'm getting the same error with just VCR.py (script below). The error happens when the request is replayed, not on the first recording.

I think what we are seeing is the result of this bug: https://github.com/kevin1024/vcrpy/pull/397. The fix was not released yet, but you can try it by installing from git: git+https://github.com/kevin1024/vcrpy/@8fdc6dbb68ee59f1f92f6dcb8d5c16b6207d22ca#egg=vcrpy.


The test I've run without plugin, I've uninstalled pytest-vcr from the environment used for tests, just in case:

import aiohttp
import vcr

async def test_mdk():
  with vcr.use_cassette('cassettes/test_mdk.yaml'):
    async with aiohttp.ClientSession() as session:
        async with session.get("https://mdk.fr") as resp:
            assert resp.status == 200
JulienPalard commented 5 years ago

Looks like you're right, I may have tried too fast and was unable to reproduce the issue with vcrpy only.