Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

asyncio.exceptions.InvalidStateError: invalid state when using async singleton #941

Copy link
Copy link
@chbndrhnns

Description

@chbndrhnns
Issue body actions

dependency-injector 4.48.3

Bug: When an async Singleton's returned Future is cancelled before the
underlying async factory completes, the _async_init_instance callback
still fires and calls future_result.set_result() on the already-cancelled
Future, raising asyncio.InvalidStateError.
We also need anyio to reproduce.

How it works:

  • Call async singleton → DI returns future_result to caller
  • Cancel future_result before slow_init() completes
  • Wait for slow_init() finish in the background
  • DI's _async_init_instance callback fires and calls set_result() on the already-cancelled future_result
    → InvalidStateError
"""Minimal reproduction of dependency-injector InvalidStateError.

Bug: When an async Singleton's returned Future is cancelled before the
underlying async factory completes, the `_async_init_instance` callback
still fires and calls `future_result.set_result()` on the already-cancelled
Future, raising `asyncio.InvalidStateError`.

Run with:
    pytest test_invalid_state.py -v
"""

import asyncio

import pytest
from dependency_injector import containers, providers


async def slow_init():
    """Async resource that takes time to initialize."""
    await asyncio.sleep(0.1)  # Simulate slow async init
    return object()


class Service:
    """Simple service class that takes a backend dependency."""

    def __init__(self, backend):
        self.backend = backend


class Container(containers.DeclarativeContainer):
    backend = providers.Resource(slow_init)
    # Singleton with async dependency -> triggers _async_init_instance internally
    service = providers.Singleton(Service, backend=backend)


@pytest.fixture
def container_instance():
    """Fresh container for each test."""
    return Container()


@pytest.fixture()
def anyio_backend():
    return "asyncio"

@pytest.mark.anyio
class TestInvalidStateOnCancel:
    async def test_cancel_before_init_completes(self, container_instance):
        future = container_instance.service()
        await asyncio.sleep(0.01)
        future.cancel()
        await asyncio.sleep(0.2)

Output:

FAILED [100%]
tests/test_.py:44 (TestInvalidStateOnCancel.test_cancel_before_init_completes)
pyfuncitem = <Function test_cancel_before_init_completes>

    @pytest.hookimpl(tryfirst=True)
    def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None:
        def run_with_hypothesis(**kwargs: Any) -> None:
            with get_runner(backend_name, backend_options) as runner:
                runner.run_test(original_func, kwargs)
    
        backend = pyfuncitem.funcargs.get("anyio_backend")
        if backend:
            backend_name, backend_options = extract_backend_and_options(backend)
    
            if hasattr(pyfuncitem.obj, "hypothesis"):
                # Wrap the inner test function unless it's already wrapped
                original_func = pyfuncitem.obj.hypothesis.inner_test
                if original_func.__qualname__ != run_with_hypothesis.__qualname__:
                    if iscoroutinefunction(original_func):
                        pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis
    
                return None
    
            if iscoroutinefunction(pyfuncitem.obj):
                funcargs = pyfuncitem.funcargs
                testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
                with get_runner(backend_name, backend_options) as runner:
                    try:
>                       runner.run_test(pyfuncitem.obj, testargs)

../../../../../Library/Caches/pypoetry/virtualenvs/bla-e4AtMU9r-py3.12/lib/python3.12/site-packages/anyio/pytest_plugin.py:160: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../../Library/Caches/pypoetry/virtualenvs/bla-e4AtMU9r-py3.12/lib/python3.12/site-packages/anyio/_backends/_asyncio.py:2279: in run_test
    self._raise_async_exceptions()
../../../../../Library/Caches/pypoetry/virtualenvs/bla-e4AtMU9r-py3.12/lib/python3.12/site-packages/anyio/_backends/_asyncio.py:2183: in _raise_async_exceptions
    raise exceptions[0]
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/events.py:84: in _run
    self._context.run(self._callback, *self._args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

>   ???
E   asyncio.exceptions.InvalidStateError: invalid state

src/dependency_injector/providers.pyx:2964: InvalidStateError

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.