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

"OSError: could not get source code" Came Up When "pytest_fixture_setup()" Executed

Open muazhari opened this issue 1 year ago • 1 comments

Every initial test case will fail when pytest_fixture_setup is executed. I have compared pytest==8.1.1 and pytest==8.2.0. The 8.1.1 has no error, and the 8.2.0 has an error.

My conftest.py:

import asyncio
from asyncio import AbstractEventLoop

import pytest
import pytest_asyncio

from tests.containers.test_container import TestContainer


@pytest_asyncio.fixture(scope="session")
def event_loop():
    loop: AbstractEventLoop = asyncio.get_event_loop()
    yield loop
    loop.close()


@pytest_asyncio.fixture(scope="function")
async def main_context(request: pytest.FixtureRequest):
    test_container: TestContainer = TestContainer()
    main_context = test_container.main_context()
    await main_context.all_seeder.up()
    yield main_context
    await main_context.all_seeder.down()

Comparison:

  • 8.1.1
============================= test session starts ==============================
platform linux -- Python 3.10.12, pytest-8.1.1, pluggy-1.5.0
rootdir: /app
plugins: cov-5.0.0, asyncio-0.23.6, xdist-3.6.1, anyio-4.3.0
asyncio: mode=strict
1 worker [43 items]
...........................................                              [100%]
  • 8.2.0
============================================================================================================== test session starts ===============================================================================================================
platform linux -- Python 3.10.12, pytest-8.2.0, pluggy-1.5.0
rootdir: /app
plugins: cov-5.0.0, asyncio-0.23.6, xdist-3.6.1, anyio-4.3.0
asyncio: mode=strict
1 worker [5 items]     
E....                                                                                                                                                                                                                                      [100%]
===================================================================================================================== ERRORS =====================================================================================================================
_______________________________________________________________________________________ ERROR at setup of test__find_many_with_pagination__should__succeed _______________________________________________________________________________________
[gw0] linux -- Python 3.10.12 /usr/bin/python3

fixturedef = <FixtureDef argname='event_loop' scope='session' baseid='tests'>

    @pytest.hookimpl(hookwrapper=True)
    def pytest_fixture_setup(
        fixturedef: FixtureDef,
    ) -> Generator[None, Any, None]:
        """Adjust the event loop policy when an event loop is produced."""
        if fixturedef.argname == "event_loop":
            # The use of a fixture finalizer is preferred over the
            # pytest_fixture_post_finalizer hook. The fixture finalizer is invoked once
            # for each fixture, whereas the hook may be invoked multiple times for
            # any specific fixture.
            # see https://github.com/pytest-dev/pytest/issues/5848
            _add_finalizers(
                fixturedef,
                _close_event_loop,
                _restore_event_loop_policy(asyncio.get_event_loop_policy()),
                _provide_clean_event_loop,
            )
            outcome = yield
            loop: asyncio.AbstractEventLoop = outcome.get_result()
            # Weird behavior was observed when checking for an attribute of FixtureDef.func
            # Instead, we now check for a special attribute of the returned event loop
            fixture_filename = inspect.getsourcefile(fixturedef.func)
            if not getattr(loop, "__original_fixture_loop", False):
>               _, fixture_line_number = inspect.getsourcelines(fixturedef.func)

/usr/local/lib/python3.10/dist-packages/pytest_asyncio/plugin.py:758: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/lib/python3.10/inspect.py:1121: in getsourcelines
    lines, lnum = findsource(object)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

object = <function event_loop at 0x7f7f854153f0>

    def findsource(object):
        """Return the entire source file and starting line number for an object.
    
        The argument may be a module, class, method, function, traceback, frame,
        or code object.  The source code is returned as a list of all the lines
        in the file and the line number indexes a line in that list.  An OSError
        is raised if the source code cannot be retrieved."""
    
        file = getsourcefile(object)
        if file:
            # Invalidate cache if needed.
            linecache.checkcache(file)
        else:
            file = getfile(object)
            # Allow filenames in form of "<something>" to pass through.
            # `doctest` monkeypatches `linecache` module to enable
            # inspection, so let `linecache.getlines` to be called.
            if not (file.startswith('<') and file.endswith('>')):
                raise OSError('source code not available')
    
        module = getmodule(object, file)
        if module:
            lines = linecache.getlines(file, module.__dict__)
        else:
            lines = linecache.getlines(file)
        if not lines:
>           raise OSError('could not get source code')
E           OSError: could not get source code

/usr/lib/python3.10/inspect.py:958: OSError

muazhari avatar May 05 '24 22:05 muazhari

The same error here, pytest 7.4.4 and pytest-asyncio 0.23.4. It was harassing me for hours yesterday and then suddenly gone (just as suddenly, as it started).

Klavionik avatar May 08 '24 10:05 Klavionik

pytest-asyncio 0.23.6 and pytest 8.2.0 same

gone with pytest==8.1.1

halvomez avatar May 14 '24 20:05 halvomez

Thanks for reporting this. The pytest-asyncio tests don't seem to be affected, so it's hard to find the cause of the error.

@muazhari @Klavionik @halvomez: Can any one provide a minimal code example that reproduces the issue?

seifertm avatar May 19 '24 11:05 seifertm

Fixed in pytest==8.2.1. However, I can't reproduce the error with minimal codes with pytest==8.2.0 and the same environment. I don't know why.

============================= test session starts ==============================
platform linux -- Python 3.10.12, pytest-8.2.1, pluggy-1.5.0
rootdir: /app
plugins: cov-5.0.0, asyncio-0.23.6, xdist-3.6.1, anyio-4.3.0
asyncio: mode=strict
1 worker [6 items] 
......       

muazhari avatar May 19 '24 20:05 muazhari

Since this issue seems to be fixed with more recent versions of pytest and I cannot reproduce it, I'll close the ticket until further evidence arrives.

seifertm avatar Jun 28 '24 13:06 seifertm

Also encountered this on the latest pytest (8.3.2). Causes the first test in the session to fail, but then all the subsequent ones succeed. Reverting to 0.21.2 resolves this 🤔

platform linux -- Python 3.9.6, pytest-8.3.2, pluggy-1.5.0

plugins: asyncio-0.23.8

asyncio: mode=auto

mike-oakley avatar Aug 02 '24 09:08 mike-oakley

@mike-oakley Any chance you can cook up a reproducer so we can get to the bottom of this?

seifertm avatar Aug 07 '24 05:08 seifertm

The same problem:

`$ poetry show pytest
 name         : pytest                                      
 version      : 8.3.2                                       
 description  : pytest: simple powerful testing with Python `

and

`$ poetry show pytest-asyncio
 name         : pytest-asyncio             
 version      : 0.23.8                     
 description  : Pytest support for asyncio `

c137santos avatar Aug 08 '24 14:08 c137santos

There's really not much I can do to help, unless someone can provide a minimal reproducer.

seifertm avatar Aug 11 '24 13:08 seifertm

There's really not much I can do to help, unless someone can provide a minimal reproducer.

Here is example fixture "event_loop" which we have a problem with

@pytest.fixture(scope='session')
def event_loop():
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
    yield loop
    loop.close()

Try to run any tests, you might get error always in first test, because problem in moment initialization.

pytest==8.1.1 work good, 8.2.1 sometimes work, 8.2.2 doesn't work

You can try this and reproduce: conftest.py

import asyncio
from httpx import AsyncClient

@pytest.fixture(scope='session')
def event_loop():
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
    yield loop
    loop.close()

@pytest.fixture(scope='session')
async def http_client():
    async with AsyncClient(app=app, base_url='https://test.io/') as client:
        yield client

test_ping.py

from httpx import AsyncClient

async def test__ping(http_client: AsyncClient):
    response = await http_client.get('/ping')
    assert response.status_code == 200
    assert response.json() == {'msg': 'pong'}

Even this tiny test return this error

devTarik avatar Aug 28 '24 23:08 devTarik

@devTarik Thanks for backing this issue up with some more code. However, when I try to run your example, I get the following error:

    @pytest.fixture(scope='session')
    async def http_client():
>       async with AsyncClient(app=app, base_url='https://test.io/') as client:
E       NameError: name 'app' is not defined

conftest.py:18: NameError

Is there a minimal implementation of app you can share?

seifertm avatar Sep 19 '24 10:09 seifertm