Attributes

Now the fun begins. The injected field uses metadata to give more instructions to the DI system. Let’s put that to work to decrease the surface area between the dataclass and the system by getting just what we need off of Customer.

We’ll change 4 lines in models.py and nothing else:

# models.py
from dataclasses import dataclass

from wired.dataclasses import Context, factory, injected


@factory()
@dataclass
class Settings:
    """Store some configuration settings for the app"""

    punctuation: str = '.'


@dataclass
class Customer:
    """A basic customer"""

    name: str = 'Larry'


@dataclass
class FrenchCustomer:
    """A certain kind of customer"""

    name: str = 'Anne'


@factory(context=Customer)
@dataclass
class Greeter:
    """A basic greeter"""

    settings: Settings
    customer_name: str = injected(Context, attr='name')
    name: str = 'Mary'

    def __call__(self):
        punctuation = self.settings.punctuation
        m = f'my name is {self.name}{punctuation}'
        return f'Hello {self.customer_name} {m}'


@factory(for_=Greeter, context=FrenchCustomer)
@dataclass
class FrenchGreeter:
    """A greeter to use when the customer (context) is French"""

    settings: Settings
    customer_name: str = injected(Context, attr='name')
    name: str = 'Henri'

    def __call__(self):
        punctuation = self.settings.punctuation
        m = f'je m\'apelle {self.name}{punctuation}'
        return f'Salut {self.customer_name} {m}'

Instead of storing the Customer and later getting the customer’s name, we use attr to get just the name off the Context. The dataclass then has customer_name: str as its constructed field, which is the only thing needed in the “template”. Smaller surface are against the outside world.

Here is what wired.dataclasses actually does behind the scenes:

from dataclasses import field

@factory()
@dataclass
class Greeter:
    customer_name: str = field(metadata=dict(injected=dict(type_=Context, attr='name')))

Using injected with an argument is easier on the eyes. The injected field uses the metadata support in dataclass fields to make a custom protocol, giving special instructions to the DI system on how to construct the dataclass.

Note

attr isn’t the only argument you can add to injected. key= also works for dictionary access.