Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Context Managers as Dependencies #3528

Open
cofin opened this issue May 25, 2024 · 1 comment
Open

Enhancement: Context Managers as Dependencies #3528

cofin opened this issue May 25, 2024 · 1 comment
Labels
Enhancement This is a new feature or request

Comments

@cofin
Copy link
Member

cofin commented May 25, 2024

Summary

We currently support generator based dependencies, but not context managers. For instance the following works:

     async def provide_connection(
        self,
        state: State,
        scope: Scope,
    ) -> AsyncGenerator[Union[PoolConnectionProxy,Connection], None]:  # noqa: UP007
        """Create a connection instance.

        Args:
            state: The ``Litestar.state`` instance.
            scope: The current connection's scope.

        Returns:
            A connection instance.
        """
        connection = cast("Optional[Union[PoolConnectionProxy,Connection]]", get_scope_state(scope, CONNECTION_SCOPE_KEY))
        if connection is None:
            pool = cast("Pool", state.get(self.pool_app_state_key))

            async with pool.acquire() as connection:
                set_scope_state(scope, CONNECTION_SCOPE_KEY, connection)
                yield connection

But adding an context manager decorator like this:

    @asynccontextmanager
    async def provide_connection(
        self,
        state: State,
        scope: Scope,
    ) -> AsyncGenerator[Union[PoolConnectionProxy,Connection], None]:  # noqa: UP007
        """Create a connection instance.

        Args:
            state: The ``Litestar.state`` instance.
            scope: The current connection's scope.

        Returns:
            A connection instance.
        """
        connection = cast("Optional[Union[PoolConnectionProxy,Connection]]", get_scope_state(scope, CONNECTION_SCOPE_KEY))
        if connection is None:
            pool = cast("Pool", state.get(self.pool_app_state_key))

            async with pool.acquire() as connection:
                set_scope_state(scope, CONNECTION_SCOPE_KEY, connection)
                yield connection

produces

msgspec.ValidationError: Unsupported type: <class 'contextlib._AsyncGeneratorContextManager'> - at `$.db_connection`

Basic Example

No response

Drawbacks and Impact

No response

Unresolved questions

No response


Note

While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.
Fund with Polar
@cofin cofin added the Enhancement This is a new feature or request label May 25, 2024
@provinzkraut
Copy link
Member

I don't think we can reasonably support this without requiring to add some abstractions that make it rather useless as a feature. The issue here is that we cannot distinguish between a dependency that is meant to be used as a contextmanager, and a dependency that just happens to be a contextmanager.

E.g.

from sqlalchemy.orm import Session

def provide_session() -> Session:
 session = get_session() # gets session from somewhere
 return session

app = Litestar(depenencies={"session": provide_session})

this wouldn't work, because Session is a context manager, and Litestar would try to call its __enter__ and __exit__ methods, causing unintended side-effects.

We could solve this by providing some wrapper that indicates that this dependency provider should be treated as a context manager, but then again you can achieve the same thing with

async def provide_connection() -> Union[PoolConnectionProxy, Connection]:
 async with thing.provide_connection() as conn:
  yield conn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement This is a new feature or request
Projects
Status: Backlog
Development

No branches or pull requests

2 participants