schireson / pytest-alembic

Pytest plugin to test alembic migrations (with default tests) and which enables you to write tests specific to your migrations.
MIT License
191 stars 13 forks source link

Incompatible with anyio due to event loop keep-alive for session fixtures #119

Open gaborbernat opened 2 days ago

gaborbernat commented 2 days ago

anyio creates an event loop during the fixture, and that loop will be reused/kept alive until the fixture is cleaned up (see https://anyio.readthedocs.io/en/stable/testing.html#technical-details). This means that if you have a session scooped fixture (which is quite common, as you might have a DB setup fixture):

@pytest.fixture(scope="session", name="db")
async def _setup_db() -> None:
    if await database_exists(ENGINE.url):  # pragma: no branch
        await drop_database(ENGINE.url)  # pragma: no cover
    await setup_db()

Then this fixture will keep alive the event loop until the end of the session (such fixture requires all your tests using the DB to reuse the same event loop).

pytest-alembic, however, assumes that when its test is running, there is no other event loop running, as it is calling https://github.com/schireson/pytest-alembic/blob/5ff6ca08f553925095389da6048895d41de2b505/src/pytest_alembic/executor.py#L182 which is only allowed to be called when there is no other live event loop.

Instead, pytest-alembic IMHO should check if there is a live event loop and reuse it if so. A working workaround for now would be to create a new thread (that by default has no running loop on it):

    with ThreadPoolExecutor() as thread_pool:
        thread_pool.submit(upgrade, alembic_runner).result()
DanCardin commented 1 day ago

I almost wonder if the thread pool option would be preferable to trying to attach to the running loop. dealing with pytest-asyncio has already been a nightmare, and i definitely feel like I've tried to make it work more integratedly before, and it ran into many (potentially pytest-asyncio specific) issues.

gaborbernat commented 1 day ago

I almost wonder if the thread pool option would be preferable to trying to attach to the running loop. dealing with pytest-asyncio has already been a nightmare, and i definitely feel like I've tried to make it work more integratedly before, and it ran into many (potentially pytest-asyncio specific) issues.

yeah, that could be a good solution