From 893f0c922f827c84771bb09c366280cb6f954490 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Wed, 29 Nov 2017 20:58:50 +0200 Subject: [PATCH 1/4] Fix #2565: Fix compatibility with pytest 3.3+ --- CHANGES/2565.bugfix | 1 + aiohttp/pytest_plugin.py | 59 +++++++++++++++++++------------------ tests/test_pytest_plugin.py | 42 ++++++++++---------------- 3 files changed, 48 insertions(+), 54 deletions(-) create mode 100644 CHANGES/2565.bugfix diff --git a/CHANGES/2565.bugfix b/CHANGES/2565.bugfix new file mode 100644 index 00000000000..4fe65a7d1c6 --- /dev/null +++ b/CHANGES/2565.bugfix @@ -0,0 +1 @@ +Fix compatibility with `pytest` 3.3+ diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py index 9c8679562fb..c88d7e82756 100644 --- a/aiohttp/pytest_plugin.py +++ b/aiohttp/pytest_plugin.py @@ -97,7 +97,13 @@ def finalizer(): @pytest.fixture def fast(request): """ --fast config option """ - return request.config.getoption('--fast') # pragma: no cover + return request.config.getoption('--fast') + + +@pytest.fixture +def loop_debug(request): + """ --enable-loop-debug config option """ + return request.config.getoption('--enable-loop-debug') @contextlib.contextmanager @@ -162,50 +168,47 @@ def pytest_pyfunc_call(pyfuncitem): return True -def pytest_configure(config): - loops = config.getoption('--loop') +def pytest_generate_tests(metafunc): + if 'loop_factory' not in metafunc.fixturenames: + return - factories = {'pyloop': asyncio.new_event_loop} + loops = metafunc.config.option.loop + avail_factories = {'pyloop': asyncio.new_event_loop} if uvloop is not None: # pragma: no cover - factories['uvloop'] = uvloop.new_event_loop + avail_factories['uvloop'] = uvloop.new_event_loop if tokio is not None: # pragma: no cover - factories['tokio'] = tokio.new_event_loop - - LOOP_FACTORIES.clear() - LOOP_FACTORY_IDS.clear() + avail_factories['tokio'] = tokio.new_event_loop if loops == 'all': loops = 'pyloop,uvloop?,tokio?' + factories = {} for name in loops.split(','): required = not name.endswith('?') name = name.strip(' ?') - if name in factories: - LOOP_FACTORIES.append(factories[name]) - LOOP_FACTORY_IDS.append(name) - elif required: - raise ValueError( - "Unknown loop '%s', available loops: %s" % ( - name, list(factories.keys()))) - asyncio.set_event_loop(None) + if name not in avail_factories: + if required: + raise ValueError( + "Unknown loop '%s', available loops: %s" % ( + name, list(factories.keys()))) + else: + continue + factories[name] = avail_factories[name] + metafunc.parametrize("loop_factory", + list(factories.values()), + ids=list(factories.keys())) -LOOP_FACTORIES = [] -LOOP_FACTORY_IDS = [] - - -@pytest.fixture(params=LOOP_FACTORIES, ids=LOOP_FACTORY_IDS) -def loop(request): +@pytest.fixture +def loop(loop_factory, fast, loop_debug): """Return an instance of the event loop.""" - fast = request.config.getoption('--fast') - debug = request.config.getoption('--enable-loop-debug') - - with loop_context(request.param, fast=fast) as _loop: - if debug: + with loop_context(loop_factory, fast=fast) as _loop: + if loop_debug: _loop.set_debug(True) # pragma: no cover yield _loop + asyncio.set_event_loop(None) @pytest.fixture diff --git a/tests/test_pytest_plugin.py b/tests/test_pytest_plugin.py index a62d5ab3b38..a15794bd46c 100644 --- a/tests/test_pytest_plugin.py +++ b/tests/test_pytest_plugin.py @@ -3,11 +3,13 @@ import pytest -from aiohttp.pytest_plugin import LOOP_FACTORIES - pytest_plugins = 'pytester' +CONFTEST = ''' +pytest_plugins = 'aiohttp.pytest_plugin' +''' + def test_aiohttp_plugin(testdir): testdir.makepyfile("""\ @@ -17,10 +19,6 @@ def test_aiohttp_plugin(testdir): from aiohttp import web - -pytest_plugins = 'aiohttp.pytest_plugin' - - @asyncio.coroutine def hello(request): return web.Response(body=b'Hello, world') @@ -72,7 +70,7 @@ def test_hello_fails(test_client): @asyncio.coroutine def test_hello_with_fake_loop(test_client): - with pytest.raises(AssertionError): + with pytest.raises(RuntimeError): fake_loop = mock.Mock() yield from test_client(web.Application(loop=fake_loop)) @@ -153,11 +151,9 @@ def make_app(loop): yield from test_client(make_app) """) - testdir.runpytest('-p', 'no:sugar') - - # i dont know how to fix this - # result = testdir.runpytest('-p', 'no:sugar') - # result.assert_outcomes(passed=11, failed=1) + testdir.makeconftest(CONFTEST) + result = testdir.runpytest_subprocess('-p', 'no:sugar', '--loop=pyloop') + result.assert_outcomes(passed=11, failed=1) @pytest.mark.skipif(sys.version_info < (3, 5), reason='old python') @@ -165,8 +161,6 @@ def test_warning_checks(testdir, capsys): testdir.makepyfile("""\ import asyncio -pytest_plugins = 'aiohttp.pytest_plugin' - async def foobar(): return 123 @@ -177,7 +171,8 @@ async def test_good(): async def test_bad(): foobar() """) - result = testdir.runpytest('-p', 'no:sugar', '-s') + testdir.makeconftest(CONFTEST) + result = testdir.runpytest('-p', 'no:sugar', '-s', '--loop=pyloop') result.assert_outcomes(passed=1, failed=1) stdout, _ = capsys.readouterr() assert ("test_warning_checks.py:__LINE__:coroutine 'foobar' was " @@ -192,9 +187,6 @@ def test_aiohttp_plugin_async_fixture(testdir, capsys): from aiohttp import web -pytest_plugins = 'aiohttp.pytest_plugin' - - @asyncio.coroutine def hello(request): return web.Response(body=b'Hello, world') @@ -244,9 +236,9 @@ def test_foo_without_loop(foo): def test_bar(loop, bar): assert bar is test_bar """) - nb_loops = len(LOOP_FACTORIES) - result = testdir.runpytest('-p', 'no:sugar') - result.assert_outcomes(passed=3 * nb_loops, error=1) + testdir.makeconftest(CONFTEST) + result = testdir.runpytest_subprocess('-p', 'no:sugar', '--loop=pyloop') + result.assert_outcomes(passed=3, error=1) result.stdout.fnmatch_lines( "*Asynchronous fixtures must depend on the 'loop' fixture " "or be used in tests depending from it." @@ -263,8 +255,6 @@ def test_aiohttp_plugin_async_gen_fixture(testdir): from aiohttp import web -pytest_plugins = 'aiohttp.pytest_plugin' - canary = mock.Mock() @@ -292,6 +282,6 @@ async def test_hello(cli): def test_finalized(): assert canary.called is True """) - nb_loops = len(LOOP_FACTORIES) - result = testdir.runpytest('-p', 'no:sugar') - result.assert_outcomes(passed=1 * nb_loops + 1) + testdir.makeconftest(CONFTEST) + result = testdir.runpytest_subprocess('-p', 'no:sugar', '--loop=pyloop') + result.assert_outcomes(passed=2) From d545fa09d4c8f6e63ce5b9cac8aec9a4fa75cb6c Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Wed, 29 Nov 2017 21:01:01 +0200 Subject: [PATCH 2/4] Polish docstring --- aiohttp/pytest_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py index c88d7e82756..46768c12eba 100644 --- a/aiohttp/pytest_plugin.py +++ b/aiohttp/pytest_plugin.py @@ -96,13 +96,13 @@ def finalizer(): @pytest.fixture def fast(request): - """ --fast config option """ + """--fast config option""" return request.config.getoption('--fast') @pytest.fixture def loop_debug(request): - """ --enable-loop-debug config option """ + """--enable-loop-debug config option""" return request.config.getoption('--enable-loop-debug') From dde13160b4ead5e476f3994f580380eeae075331 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Wed, 29 Nov 2017 21:27:07 +0200 Subject: [PATCH 3/4] Fix coverage plugin --- tests/test_pytest_plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_pytest_plugin.py b/tests/test_pytest_plugin.py index a15794bd46c..ef696ee5928 100644 --- a/tests/test_pytest_plugin.py +++ b/tests/test_pytest_plugin.py @@ -152,7 +152,7 @@ def make_app(loop): """) testdir.makeconftest(CONFTEST) - result = testdir.runpytest_subprocess('-p', 'no:sugar', '--loop=pyloop') + result = testdir.runpytest('-p', 'no:sugar', '--loop=pyloop') result.assert_outcomes(passed=11, failed=1) @@ -237,7 +237,7 @@ def test_bar(loop, bar): assert bar is test_bar """) testdir.makeconftest(CONFTEST) - result = testdir.runpytest_subprocess('-p', 'no:sugar', '--loop=pyloop') + result = testdir.runpytest('-p', 'no:sugar', '--loop=pyloop') result.assert_outcomes(passed=3, error=1) result.stdout.fnmatch_lines( "*Asynchronous fixtures must depend on the 'loop' fixture " @@ -283,5 +283,5 @@ def test_finalized(): assert canary.called is True """) testdir.makeconftest(CONFTEST) - result = testdir.runpytest_subprocess('-p', 'no:sugar', '--loop=pyloop') + result = testdir.runpytest('-p', 'no:sugar', '--loop=pyloop') result.assert_outcomes(passed=2) From ceb986df336c4d60b827204e556552a7c001dcdc Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Thu, 30 Nov 2017 00:45:19 +0200 Subject: [PATCH 4/4] Disable coverage blame --- aiohttp/pytest_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py index 46768c12eba..d60ef03c64a 100644 --- a/aiohttp/pytest_plugin.py +++ b/aiohttp/pytest_plugin.py @@ -188,7 +188,7 @@ def pytest_generate_tests(metafunc): for name in loops.split(','): required = not name.endswith('?') name = name.strip(' ?') - if name not in avail_factories: + if name not in avail_factories: # pragma: no cover if required: raise ValueError( "Unknown loop '%s', available loops: %s" % (