From 377e029429cab3e928e3fe1b05895e9a9d88b6a0 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Sun, 27 Oct 2024 16:38:47 -0400 Subject: [PATCH] Upgrade pytest-asyncio --- pyproject.toml | 1 + tests/api/test_listeners.py | 12 +++---- tests/api/test_request.py | 24 +++++++------- tests/api/test_response.py | 6 ++-- tests/application/test_requests.py | 10 +++--- tests/conftest.py | 50 +++--------------------------- tests/test_uart.py | 20 ++++++------ 7 files changed, 41 insertions(+), 82 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 50c5afa9..24c596d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ timeout = 20 log_format = "%(asctime)s.%(msecs)03d %(levelname)s %(message)s" log_date_format = "%Y-%m-%d %H:%M:%S" asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" [tool.flake8] exclude = ".venv,.git,.tox,docs,venv,bin,lib,deps,build" diff --git a/tests/api/test_listeners.py b/tests/api/test_listeners.py index 0485d211..beabea8f 100644 --- a/tests/api/test_listeners.py +++ b/tests/api/test_listeners.py @@ -8,13 +8,13 @@ from zigpy_znp.api import OneShotResponseListener, CallbackResponseListener -async def test_resolve(event_loop, mocker): +async def test_resolve(mocker): callback = mocker.Mock() callback_listener = CallbackResponseListener( [c.SYS.Ping.Rsp(partial=True)], callback ) - future = event_loop.create_future() + future = asyncio.get_running_loop().create_future() one_shot_listener = OneShotResponseListener([c.SYS.Ping.Rsp(partial=True)], future) match = c.SYS.Ping.Rsp(Capabilities=t.MTCapabilities.SYS) @@ -42,9 +42,9 @@ async def test_resolve(event_loop, mocker): assert one_shot_listener.cancel() -async def test_cancel(event_loop): +async def test_cancel(): # Cancelling a one-shot listener prevents it from being fired - future = event_loop.create_future() + future = asyncio.get_running_loop().create_future() one_shot_listener = OneShotResponseListener([c.SYS.Ping.Rsp(partial=True)], future) one_shot_listener.cancel() @@ -55,13 +55,13 @@ async def test_cancel(event_loop): await future -async def test_multi_cancel(event_loop, mocker): +async def test_multi_cancel(mocker): callback = mocker.Mock() callback_listener = CallbackResponseListener( [c.SYS.Ping.Rsp(partial=True)], callback ) - future = event_loop.create_future() + future = asyncio.get_running_loop().create_future() one_shot_listener = OneShotResponseListener([c.SYS.Ping.Rsp(partial=True)], future) match = c.SYS.Ping.Rsp(Capabilities=t.MTCapabilities.SYS) diff --git a/tests/api/test_request.py b/tests/api/test_request.py index de64b9c2..a87fe307 100644 --- a/tests/api/test_request.py +++ b/tests/api/test_request.py @@ -11,7 +11,7 @@ from zigpy_znp.exceptions import CommandNotRecognized, InvalidCommandResponse -async def test_callback_rsp(connected_znp, event_loop): +async def test_callback_rsp(connected_znp): znp, znp_server = connected_znp def send_responses(): @@ -20,7 +20,7 @@ def send_responses(): c.AF.DataConfirm.Callback(Endpoint=56, TSN=1, Status=t.Status.SUCCESS) ) - event_loop.call_soon(send_responses) + asyncio.get_running_loop().call_soon(send_responses) # The UART sometimes replies with a SRSP and an AREQ faster than # we can register callbacks for both. This method is a workaround. @@ -150,7 +150,7 @@ async def replier(req): assert len(znp._unhandled_command.mock_calls) == 0 -async def test_callback_rsp_cleanup_concurrent(connected_znp, event_loop, mocker): +async def test_callback_rsp_cleanup_concurrent(connected_znp, mocker): znp, znp_server = connected_znp mocker.spy(znp, "_unhandled_command") @@ -163,7 +163,7 @@ def send_responses(): znp_server.send(c.SYS.OSALTimerExpired.Callback(Id=0xAB)) znp_server.send(c.SYS.OSALTimerExpired.Callback(Id=0xCD)) - event_loop.call_soon(send_responses) + asyncio.get_running_loop().call_soon(send_responses) callback_rsp = await znp.request_callback_rsp( request=c.UTIL.TimeAlive.Req(), @@ -183,7 +183,7 @@ def send_responses(): ] -async def test_znp_request_kwargs(connected_znp, event_loop): +async def test_znp_request_kwargs(connected_znp): znp, znp_server = connected_znp # Invalid format @@ -196,7 +196,7 @@ async def test_znp_request_kwargs(connected_znp, event_loop): # Valid format, valid name ping_rsp = c.SYS.Ping.Rsp(Capabilities=t.MTCapabilities.SYS) - event_loop.call_soon(znp_server.send, ping_rsp) + asyncio.get_running_loop().call_soon(znp_server.send, ping_rsp) assert ( await znp.request(c.SYS.Ping.Req(), RspCapabilities=t.MTCapabilities.SYS) ) == ping_rsp @@ -227,7 +227,7 @@ async def test_znp_request_kwargs(connected_znp, event_loop): ) -async def test_znp_request_not_recognized(connected_znp, event_loop): +async def test_znp_request_not_recognized(connected_znp): znp, _ = connected_znp # An error is raise when a bad request is sent @@ -237,11 +237,11 @@ async def test_znp_request_not_recognized(connected_znp, event_loop): ) with pytest.raises(CommandNotRecognized): - event_loop.call_soon(znp.frame_received, unknown_rsp.to_frame()) + asyncio.get_running_loop().call_soon(znp.frame_received, unknown_rsp.to_frame()) await znp.request(request) -async def test_znp_request_wrong_params(connected_znp, event_loop): +async def test_znp_request_wrong_params(connected_znp): znp, _ = connected_znp # You cannot specify response kwargs for responses with no response @@ -250,14 +250,14 @@ async def test_znp_request_wrong_params(connected_znp, event_loop): # An error is raised when a response with bad params is received with pytest.raises(InvalidCommandResponse): - event_loop.call_soon( + asyncio.get_running_loop().call_soon( znp.frame_received, c.SYS.Ping.Rsp(Capabilities=t.MTCapabilities.SYS).to_frame(), ) await znp.request(c.SYS.Ping.Req(), RspCapabilities=t.MTCapabilities.APP) -async def test_znp_sreq_srsp(connected_znp, event_loop): +async def test_znp_sreq_srsp(connected_znp): znp, _ = connected_znp # Each SREQ must have a corresponding SRSP, so this will fail @@ -267,7 +267,7 @@ async def test_znp_sreq_srsp(connected_znp, event_loop): # This will work ping_rsp = c.SYS.Ping.Rsp(Capabilities=t.MTCapabilities.SYS) - event_loop.call_soon(znp.frame_received, ping_rsp.to_frame()) + asyncio.get_running_loop().call_soon(znp.frame_received, ping_rsp.to_frame()) await znp.request(c.SYS.Ping.Req()) diff --git a/tests/api/test_response.py b/tests/api/test_response.py index 264ff3ef..d8ffad40 100644 --- a/tests/api/test_response.py +++ b/tests/api/test_response.py @@ -190,7 +190,7 @@ async def test_wait_responses_empty(connected_znp): await znp.wait_for_responses([]) -async def test_response_callback_simple(connected_znp, event_loop, mocker): +async def test_response_callback_simple(connected_znp, mocker): znp, _ = connected_znp sync_callback = mocker.Mock() @@ -207,7 +207,7 @@ async def test_response_callback_simple(connected_znp, event_loop, mocker): sync_callback.assert_called_once_with(good_response) -async def test_response_callbacks(connected_znp, event_loop, mocker): +async def test_response_callbacks(connected_znp, mocker): znp, _ = connected_znp sync_callback = mocker.Mock() @@ -270,7 +270,7 @@ async def async_callback(response): assert len(async_callback_responses) == 3 -async def test_wait_for_responses(connected_znp, event_loop): +async def test_wait_for_responses(connected_znp): znp, _ = connected_znp response1 = c.SYS.Ping.Rsp(Capabilities=t.MTCapabilities.SYS) diff --git a/tests/application/test_requests.py b/tests/application/test_requests.py index e123a30b..c17765a1 100644 --- a/tests/application/test_requests.py +++ b/tests/application/test_requests.py @@ -206,7 +206,7 @@ async def test_mrequest(device, make_application, mocker): @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1]) -async def test_mrequest_doesnt_block(device, make_application, event_loop): +async def test_mrequest_doesnt_block(device, make_application): app, znp_server = make_application(server_cls=device) znp_server.reply_once_to( @@ -226,7 +226,7 @@ async def test_mrequest_doesnt_block(device, make_application, event_loop): Status=t.Status.SUCCESS, Endpoint=1, TSN=2 ) - request_sent = event_loop.create_future() + request_sent = asyncio.get_running_loop().create_future() request_sent.add_done_callback(lambda _: znp_server.send(data_confirm_rsp)) await app.startup(auto_form=False) @@ -398,9 +398,7 @@ async def test_nonstandard_profile(device, make_application): @pytest.mark.parametrize("device", FORMED_DEVICES) -async def test_request_cancellation_shielding( - device, make_application, mocker, event_loop -): +async def test_request_cancellation_shielding(device, make_application, mocker): app, znp_server = make_application(server_cls=device) await app.startup(auto_form=False) @@ -412,7 +410,7 @@ async def test_request_cancellation_shielding( device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xABCD) - delayed_reply_sent = event_loop.create_future() + delayed_reply_sent = asyncio.get_running_loop().create_future() def delayed_reply(req): async def inner(): diff --git a/tests/conftest.py b/tests/conftest.py index 274478af..e71991e9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,4 @@ -import gc -import sys import json -import typing import asyncio import inspect import logging @@ -46,46 +43,6 @@ def pytest_collection_modifyitems(session, config, items): item.add_marker(pytest.mark.filterwarnings("error::RuntimeWarning")) -@pytest.hookimpl(trylast=True) -def pytest_fixture_post_finalizer(fixturedef, request) -> None: - """Called after fixture teardown""" - if fixturedef.argname != "event_loop": - return - - policy = asyncio.get_event_loop_policy() - try: - loop = policy.get_event_loop() - except RuntimeError: - loop = None - if loop is not None: - # Cleanup code based on the implementation of asyncio.run() - try: - if not loop.is_closed(): - asyncio.runners._cancel_all_tasks(loop) # type: ignore[attr-defined] - loop.run_until_complete(loop.shutdown_asyncgens()) - if sys.version_info >= (3, 9): - loop.run_until_complete(loop.shutdown_default_executor()) - finally: - loop.close() - new_loop = policy.new_event_loop() # Replace existing event loop - # Ensure subsequent calls to get_event_loop() succeed - policy.set_event_loop(new_loop) - - -@pytest.fixture -def event_loop( - request: pytest.FixtureRequest, -) -> typing.Iterator[asyncio.AbstractEventLoop]: - """Create an instance of the default event loop for each test case.""" - yield asyncio.get_event_loop_policy().new_event_loop() - # Call the garbage collector to trigger ResourceWarning's as soon - # as possible (these are triggered in various __del__ methods). - # Without this, resources opened in one test can fail other tests - # when the warning is generated. - gc.collect() - # Event loop cleanup handled by pytest_fixture_post_finalizer - - class ForwardingSerialTransport: """ Serial transport that hooks directly into a protocol @@ -237,10 +194,11 @@ async def inner(server_cls): @pytest.fixture -def connected_znp(event_loop, make_connected_znp): - znp, znp_server = event_loop.run_until_complete(make_connected_znp(BaseServerZNP)) +async def connected_znp(make_connected_znp): + znp, znp_server = await make_connected_znp(BaseServerZNP) yield znp, znp_server - znp.close() + await znp.disconnect() + await znp_server.disconnect() def simple_deepcopy(d): diff --git a/tests/test_uart.py b/tests/test_uart.py index 633fed73..80efbb1d 100644 --- a/tests/test_uart.py +++ b/tests/test_uart.py @@ -1,3 +1,5 @@ +import asyncio + import pytest from serial_asyncio import SerialTransport @@ -19,25 +21,25 @@ def connected_uart(mocker): @pytest.fixture -def dummy_serial_conn(event_loop, mocker): +async def dummy_serial_conn(mocker): device = "/dev/ttyACM0" serial_interface = mocker.Mock() serial_interface.name = device def create_serial_conn(loop, protocol_factory, url, *args, **kwargs): - fut = event_loop.create_future() + fut = loop.create_future() assert url == device protocol = protocol_factory() # Our event loop doesn't really do anything - event_loop.add_writer = lambda *args, **kwargs: None - event_loop.add_reader = lambda *args, **kwargs: None - event_loop.remove_writer = lambda *args, **kwargs: None - event_loop.remove_reader = lambda *args, **kwargs: None + loop.add_writer = lambda *args, **kwargs: None + loop.add_reader = lambda *args, **kwargs: None + loop.remove_writer = lambda *args, **kwargs: None + loop.remove_reader = lambda *args, **kwargs: None - transport = SerialTransport(event_loop, protocol, serial_interface) + transport = SerialTransport(loop, protocol, serial_interface) protocol.connection_made(transport) @@ -221,11 +223,11 @@ def test_uart_frame_received_error(connected_uart, mocker): assert znp.frame_received.call_count == 3 -async def test_connection_lost(dummy_serial_conn, mocker, event_loop): +async def test_connection_lost(dummy_serial_conn, mocker): device, _ = dummy_serial_conn znp = mocker.Mock() - conn_lost_fut = event_loop.create_future() + conn_lost_fut = asyncio.get_running_loop().create_future() znp.connection_lost = conn_lost_fut.set_result protocol = await znp_uart.connect(