Lazy initialization

If you want to create some class instance with filled required data during init, and then populated with config values, you can use the parameter lazy_init for this purpose. All file constants will be injected after calling the method init_props. Example of work:

application.yml:

default_text_style:
  size: 14
  weight: bold
  font: "Times New Roman"
  color:
    - 128
    - 128
    - 128
language_greetings:
  - language: english
    text: hello
  - language: german
    text: hallo
  - language: french
    text: bonjour
wellcome_message: "{greeting}! Thank you for registration, {username}!"
mailing_frequency:
  days: 5
  hours: 12

lazy_init_demo.py:

from typing import List, Optional, Tuple, TypedDict, Union

from dataclasses import dataclass
from datetime import timedelta

from conjector import properties


@dataclass
class TextStyle:
    size: int
    weight: str
    font: str
    color: Union[Tuple[int, int, int], str]


class GreetingDict(TypedDict):
    language: str
    text: str


@properties(lazy_init=True)
@dataclass
class EmailMessageServiceConfig:
    default_text_style: TextStyle
    language_greetings: List[GreetingDict]
    mailing_frequency: Optional[timedelta] = None
    wellcome_message: str = "some_default_message"


email_config = EmailMessageServiceConfig(
    default_text_style=TextStyle(
        size=16, weight="normal", font="Arial", color="black"
    ),
    language_greetings=[GreetingDict(language="english", text="hello")],
)
# it works like a normal dataclass instance
assert email_config.default_text_style == TextStyle(
    size=16, weight="normal", font="Arial", color="black"
)
assert email_config.mailing_frequency is None
assert email_config.wellcome_message == "some_default_message"

# after calling `init_props`, config values will be injected.
# It also overrides all values that we set during initialize before.
email_config.init_props()
assert email_config.default_text_style == TextStyle(
    size=14, weight="bold", font="Times New Roman", color=(128, 128, 128)
)
assert email_config.mailing_frequency == timedelta(days=5, hours=12)
assert email_config.wellcome_message == (
    "{greeting}! Thank you for registration, {username}!"
)

Because there are 3 sources of data (default values, values passed during initialization, and config file values), it could be hard to understand how we can resolve this conflict. Bellow is the table to clarify the behavior of the init_props method.

init

default

config

will be used

-

+

-

default

-

+

+

config

+

~

-

init

+

~

+

init \ config

+- provided; -- missing; ~- not affect.

How you can see, when both init and config values provided, they are equally important, but, by default, config have higher priority and overrides init. If you, for some reason, don’t want to override already initialized values, only defaults, it’s also possible with init_props(override_init=False)