alt-pytest-asyncio icon indicating copy to clipboard operation
alt-pytest-asyncio copied to clipboard

ContextVar not propagated from fixture to test

Open andredias opened this issue 3 years ago • 1 comments

As pytest-asyncio, it seems that alt-pytest-asyncio also doesn't propagate ContextVar to tests. Instead of duplicating the problem description, I will list the references to the issue below:

andredias avatar Oct 15 '21 15:10 andredias

Hello,

I've never worked with context vars before. I might have time to look today, this sounds more interesting than what I was gonna do hahah

delfick avatar Oct 15 '21 22:10 delfick

I've got a new issue on alt-pytest-asyncio that might be related to #8 since the library databases relies on ContextVars. I created a minimal example to show the problem:

from collections.abc import AsyncIterable

from databases import Database
from pytest import fixture


@fixture
async def db() -> AsyncIterable[Database]:
    db: Database = Database("sqlite:///", force_rollback=True)
    await db.connect()
    try:
        yield db
    finally:
        await db.disconnect()


async def test_db(db: Database) -> None: ...

It is just one test_1.py file that is supposed to be run by pytest test_issue.py. It is supposed to run flawlessly but I get this error:

$ pytest
=================================== test session starts ===================================
platform linux -- Python 3.12.3, pytest-8.2.0, pluggy-1.5.0
rootdir: /tmp/databases
plugins: alt-pytest-asyncio-0.7.2
collected 1 item                                                                          

tests/test_1.py .E                                                                  [100%]

========================================= ERRORS ==========================================
______________________________ ERROR at teardown of test_db _______________________________

    @fixture
    async def db() -> AsyncIterable[Database]:
        db: Database = Database("sqlite:///", force_rollback=True)
        await db.connect()
        try:
            yield db
        finally:
>           await db.disconnect()

tests/test_1.py:14: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv/lib/python3.12/site-packages/databases/core.py:141: in disconnect
    await self._global_transaction.__aexit__()
.venv/lib/python3.12/site-packages/databases/core.py:426: in __aexit__
    await self.rollback()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <databases.core.Transaction object at 0x75ea0da77470>

    async def rollback(self) -> None:
        async with self._connection._transaction_lock:
            assert self._connection._transaction_stack[-1] is self
            self._connection._transaction_stack.pop()
>           assert self._transaction is not None
E           AssertionError

.venv/lib/python3.12/site-packages/databases/core.py:473: AssertionError

The test not only fails but it also hangs.

Note that databases uses a contexVar

Setup

To run this snippet, you need:

  • databases[aiosqlite]
  • pytest
  • alt-pytest-asyncio

Can you help me solve that?

andredias avatar May 13 '24 17:05 andredias

Hello,

I should have time on the weekend to take a look :)

On Tue, 14 May 2024, 03:21 André Felipe Dias, @.***> wrote:

I've got a new issue on alt-pytest-asyncio that might be related to #8 https://github.com/delfick/alt-pytest-asyncio/issues/8 since the library databases relies on ContextVars https://github.com/encode/databases/blob/ae3fb16f40201d9ed0ed31bea31a289127169568/databases/core.py#L39. I created a minimal example to show the problem:

from collections.abc import AsyncIterable from databases import Databasefrom pytest import fixture

@fixtureasync def db() -> AsyncIterable[Database]: db: Database = Database("sqlite:///", force_rollback=True) await db.connect() try: yield db finally: await db.disconnect()

async def test_db(db: Database) -> None: ...

It is just one test_1.py file that is supposed to be run by pytest test_issue.py. It is supposed to run flawlessly but I get this error:

$ pytest =================================== test session starts =================================== platform linux -- Python 3.12.3, pytest-8.2.0, pluggy-1.5.0 rootdir: /tmp/databases plugins: alt-pytest-asyncio-0.7.2 collected 1 item

tests/test_1.py .E [100%]

========================================= ERRORS ========================================== ______________________________ ERROR at teardown of test_db _______________________________

@fixture
async def db() -> AsyncIterable[Database]:
    db: Database = Database("sqlite:///", force_rollback=True)
    await db.connect()
    try:
        yield db
    finally:>           await db.disconnect()

tests/test_1.py:14:


.venv/lib/python3.12/site-packages/databases/core.py:141: in disconnect await self._global_transaction.aexit() .venv/lib/python3.12/site-packages/databases/core.py:426: in aexit await self.rollback()


self = <databases.core.Transaction object at 0x75ea0da77470>

async def rollback(self) -> None:
    async with self._connection._transaction_lock:
        assert self._connection._transaction_stack[-1] is self
        self._connection._transaction_stack.pop()>           assert self._transaction is not None

E AssertionError

.venv/lib/python3.12/site-packages/databases/core.py:473: AssertionError

The test not only fails but it also hangs.

Note that databases uses a contexVar Setup

To run this snippet, you need:

  • databases[aiosqlite]
  • pytest
  • alt-pytest-asyncio

Can you help me solve that?

— Reply to this email directly, view it on GitHub https://github.com/delfick/alt-pytest-asyncio/issues/8#issuecomment-2108316047, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAA2V5PIRFEWGPQIB63LFPDZCDZAVAVCNFSM5GCIA4EKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMJQHAZTCNRQGQ3Q . You are receiving this because you commented.Message ID: @.***>

delfick avatar May 14 '24 11:05 delfick

Thanks!

andredias avatar May 14 '24 12:05 andredias

The exact same issue was reported here. There is another comment in the previous link that mentions anyio pytest-plugin as a possible solution.

I saw somewhere else about using asyncio.Runner to keep the context among different tasks.

andredias avatar May 14 '24 14:05 andredias

I should have time on the weekend to take a look :)

Turns out that very much wasn't the case, we'll see if I do this weekend.

delfick avatar May 21 '24 06:05 delfick

@andredias

I saw somewhere else about using asyncio.Runner to keep the context among different tasks.

I tried using asyncio.Runner against the changes already in that PR and it got messy really quick. I'd need to do some very invasive surgery to this project to use that properly and I don't have the time atm.

Also, it would require the project being python 3.11 and above. I have put in that change into the PR anyway cause it removed one of the hacks the original attempt was doing.

Does that PR work for you now? https://github.com/delfick/alt-pytest-asyncio/pull/9

delfick avatar May 26 '24 06:05 delfick

Fixed as part of the 0.8.0 release

delfick avatar Jun 03 '24 04:06 delfick