ThirVondukr / aioinject

Async-first python dependency injection library
https://thirvondukr.github.io/aioinject/
MIT License
27 stars 2 forks source link

Dataclass with default factory not supported. #12

Open nrbnlulu opened 4 months ago

nrbnlulu commented 4 months ago

example

import dataclasses

import aioinject

@dataclasses.dataclass(slots=True)
class FooBase[T]:
    bar: dict[T] = dataclasses.field(default_factory=dict)

    def baz(self):
        raise NotImplementedError()

class FooImpl(FooBase[int]):
    def baz(self):
        pass

container = aioinject.Container()
container.register(aioinject.Singleton(FooImpl))

with container.sync_context() as ctx:
    foo_impl = ctx.resolve(FooImpl)

To get around this what you could do something like

import dataclasses
from typing import TYPE_CHECKING

import aioinject

@dataclasses.dataclass(slots=True)
class FooBase[T]:
    if TYPE_CHECKING:
        bar: dict[T] = dataclasses.field(default_factory=dict)
    else:
        bar: dict = dataclasses.field(default_factory=dict)

    def baz(self):
        raise NotImplementedError()

class FooImpl(FooBase[int]):
    def baz(self):
        pass

container = aioinject.Container()
container.register(
    aioinject.Transient(dict),
    aioinject.Singleton(FooImpl)
    )

with container.sync_context() as ctx:
    foo_impl = ctx.resolve(FooImpl)

I guess this can be solved by ignoring fields that has default factories.

ThirVondukr commented 4 months ago

@nrbnlulu I think it's possible to just ignore parameters that have default arguments but honestly I'm not sure if they should be ignored or if container should try to resolve them 🤔

sig = inspect.signature(FooBase.__init__)
print(sig.parameters["bar"].default)  # <factory>
nrbnlulu commented 4 months ago

I'll try to submit a PR soon