dry-python/returns

Question. Async SQLAlchemy from sync methods

nomhoi opened this issue · 2 comments

nomhoi commented

Here this project: https://github.com/nomhoi/cosmicpython-fastapi/blob/main/chapter_13_dependency_injection/src/allocation/adapters/repository.py#L43

I have async methods in SqlAlchemyRepository:

class SqlAlchemyRepository(AbstractRepository):
    def __init__(self, session: AsyncSession):
        super().__init__()
        self.session = session
    ...
    async def _get(self, sku: str) -> model.Product:
        return (
            (await self.session.execute(select(model.Product).filter_by(sku=sku)))
            .scalars()
            .one_or_none()
        )
    ...

I need to return async methods again to sync for the repository. But I need to save dependency on AsyncSession and async SQLAlchemy.

class SqlAlchemyRepository(AbstractRepository):
    def __init__(self, session: AsyncSession):
        super().__init__()
        self.session = session
        self.event_loop = asyncio.get_event_loop()
    ...
   def _get(self, sku: str) -> model.Product:
        return (
            (await self.session.execute(select(model.Product).filter_by(sku=sku)))
            .scalars()
            .one_or_none()
        )
    ...

What is the best way to use Future or FutureResult to make it?

Hey @nomhoi, the best option in your case would be using FutureResult 'cause your computation can fail (eg. network connection can be lost, the database returns an error)! But bear in mind that even you using the Future container which allows you to "call" async functions inside non-async functions you'll need to make the computation happen by passing your result to an event loop to execute.

Also, you can use the future_safe decorator!

It'll be something like:

from returns.future import future_safe

class SqlAlchemyRepository(AbstractRepository):
    @future_safe
    async def _get(self, sku: str) -> model.Product:
        return (
            (await self.session.execute(select(model.Product).filter_by(sku=sku)))
            .scalars()
            .one_or_none()
        )

Hi @thepabloaguilar,
Thank you! I'will try this.