Fastapi: [BUG] Class based dependencies with __call__ don't work with yield

Created on 1 Apr 2020  路  5Comments  路  Source: tiangolo/fastapi

Describe the bug

Class based callable dependencies (https://fastapi.tiangolo.com/advanced/advanced-dependencies/) don't work when used as generators with yield keyword.

To Reproduce

class SessionScope:

    def __init__(self, uri):
        self.engine = create_engine(uri)
        self.session_maker = scoped_session(sessionmaker(bind=self.engine))

    def __call__(self):
        session = self.session_maker()
        try:
            yield session
        except:
            session.rollback()
            raise
        finally:
            self.session_maker.remove()

get_db = SessionScope('postgresql://postgres:[email protected]:5432/my_db')

@router.get('/', response_model=List[User])
def read_users(
    db: Session = Depends(get_db),
    skip: int = 0,
    limit: int = 100,
):
    """
    Retrieve users.
    """
    users = crud.user.get_multi(db, skip=skip, limit=limit)
    return users

Expected behavior

Dependency must return yielded session, but now it returns __call__ generator instance

Additional context

The problem lies in /fastapi/dependencies/utils.py file on line 498:

        elif inspect.isgeneratorfunction(call) or inspect.isasyncgenfunction(call):
            stack = request.scope.get("fastapi_astack")
            if stack is None:
                raise RuntimeError(
                    async_contextmanager_dependencies_error
                )  # pragma: no cover
            solved = await solve_generator(
                call=call, stack=stack, sub_values=sub_values
            )

Paramter _call_ passed to _inspect.isgeneratorfunction_ is instance of a class instead of it's __call__ method and this function always returns False.

answered bug

Most helpful comment

Thanks for the discussion here everyone! :coffee:

This was solved in https://github.com/tiangolo/fastapi/pull/1365 by @mrosales :heavy_check_mark: :tada:

It will be available in version 0.56.1 (in a couple of hours).

All 5 comments

Maybe you will find a better solution, but replacing all calls to _inspect.isgeneratorfunction_ and inspect.isasyncgenfunction_ to:

def is_generator_callable(call: Callable) -> bool:
    if inspect.ismethod(call.__call__):
        call = call.__call__
    return inspect.isgeneratorfunction(call)

def is_async_generator_callable(call: Callable) -> bool:
    if inspect.ismethod(call.__call__):
        call = call.__call__
    return inspect.isasyncgenfunction(call)

returns correct results.

Got bitten by this one as well, @neriusmika would you mind making a PR with suggested changes?

This one got me too..

Thanks for the discussion here everyone! :coffee:

This was solved in https://github.com/tiangolo/fastapi/pull/1365 by @mrosales :heavy_check_mark: :tada:

It will be available in version 0.56.1 (in a couple of hours).

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

Was this page helpful?
0 / 5 - 0 ratings