Skip to content

Bug: Provide fails to detect generator methods on callable instances #4457

@enrico-stauss

Description

@enrico-stauss

Description

The Provide class cannot properly detect when a callable instance's __call__ method is a generator function. This breaks the dependency injection with cleanup pattern when using stateful callable instances.

Reproduction

from typing import Generator, Any
from uuid import UUID, uuid4
from litestar.di import Provide

class SessionSchedulerFactory:
    """A callable factory that maintains state across calls."""
    def __init__(self):
        self._sessions: dict[UUID, object] = {}
    
    def __call__(self) -> Generator[object, Any, None]:
        """Generator method that should be detected by Provide."""
        try:
            yield uuid4()
        finally:
            pass

def session_scheduler_factory() -> Generator[object, Any, None]: 
    try:
        yield uuid4()
    finally:
        pass

# Function works correctly
Provide(session_scheduler_factory, sync_to_thread=False).has_sync_generator_dependency  # True

# Callable instance fails to be detected
Provide(SessionSchedulerFactory(), sync_to_thread=False).has_sync_generator_dependency  # False

Note: This example is simplified for reproducibility. The real use case involves maintaining a cache of sessions across multiple dependency injections, which requires instance state.

Root Cause

In Provide.__init__, isclass(instance) returns False for callable instances, so the code checks isgeneratorfunction(instance) instead of isgeneratorfunction(instance.__call__):

is_class_dependency = isclass(dependency)
self.has_sync_generator_dependency = isgeneratorfunction(
    dependency if not is_class_dependency else dependency.__call__
)

Proposed Fix

Check callable instances the same way as classes:

is_class_dependency = isclass(dependency)
is_callable_instance = not is_class_dependency and hasattr(dependency, '__call__')

check_target = dependency.__call__ if (is_class_dependency or is_callable_instance) else dependency
self.has_sync_generator_dependency = isgeneratorfunction(check_target)
self.has_async_generator_dependency = isasyncgenfunction(check_target)

Litestar Version

litestar==2.18.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bug 🐛This is something that is not working as expected

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions