maldoinc / wireup

Performant, concise, and easy-to-use dependency injection container for Python 3.8+.
https://maldoinc.github.io/wireup/
MIT License
95 stars 2 forks source link

Add support for generator factories #28

Open cfxegbert opened 2 months ago

cfxegbert commented 2 months ago

I want to do something like the following:

@service
def create_transaction() -> Iterator[Transaction]:
    transaction = Transaction()
    try:
        yield transaction
    finally:
        transaction.commit()

and also an async version:

@service
async def create_transaction() -> AsyncIterator[Transaction]:
    transaction = Transaction()
    try:
        yield transaction
    finally:
        await transaction.commit()
maldoinc commented 2 months ago

Hi @cfxegbert, generator factories are not currently supported. Coincidentally I have an experimental branch which accomplishes exactly what you're describing at least for the sync version.

In the meantime what you're describing can be accomplished in the following way:

@service
class TransactionManager:
    # pull whatever dependencies to create transaction
    def __init__(self) -> None:
        pass

    @contextlib.contextmanager
    def create_transaction(self) -> Iterator[Transaction]:
        transaction = Transaction()
        try:
            yield transaction
        finally:
            transaction.commit()

    @contextlib.asynccontextmanager
    async def create_async_transaction() -> AsyncIterator[Transaction]:
        transaction = Transaction()
        try:
            yield transaction
        finally:
            await transaction.commit()

@container.autowire  # may be omitted if using flask or fastapi integrations
def some_view(txn_manager: TransactionManager):
    with txn_manager.create_transaction() as txn:
        txn...
cfxegbert commented 2 months ago

That will work at the method level but for my situation I need it for the class/service level. You were correct in that my example was a per request example. My real situation is a singleton with an application lifetime. There are other ways to implement it but it just does not feel as clean.

maldoinc commented 2 months ago

Yeah, I understand what you're trying to do. The issue right now is that even tough the container can inject async functions it cannot create things that require async directly given that the container is synchronous.

It's definitely on the radar and something that will come in the near future.

maldoinc commented 1 day ago

@cfxegbert If this is still relevant for you, support for this has landed in master.

For now supports only sync factories (async coming soon) and works for both singletons and transient scopes. I'll do some more tests and perform a release.

In the meanwhile you can test the development version by using pip install git+https://github.com/maldoinc/wireup.git.