goodconf icon indicating copy to clipboard operation
goodconf copied to clipboard

Generate config with subclasses

Open mion00 opened this issue 1 year ago • 2 comments

Hi and thanks for this project! I am slowly migrating my configuration management to this wonderful library.

I would like to generate a yaml or toml configuration file, but I am having trouble on how to do it using the initial callable function on a nested class.

Here is a minimal example:

class AppConfig(GoodConf):
    "Main configuration file"

    class AppDaemon(BaseModel):
        "Configuration class for AppDaemon"
        latitude: float = 0
        longitude: float = 0
        elevation: float = 30
        time_zone: str = "Europe/Berlin"
   
    appdaemon: AppDaemon = Field(description="AppDaemon",  initial=lambda:AppConfig.AppDaemon())

config = AppConfig()

toml_config = config.generate_toml()

I am trying to obtain an output similar to the following:

[appdaemon]
latitude = 0
longitude = 0
elevation = 30
time_zone = "Europe/Berlin"

However I obtain the following error:

../../.pyenv/versions/3.10.8-debug/envs/appdaemon/lib/python3.10/site-packages/goodconf/__init__.py:197: in generate_toml
    toml_str = tomlkit.dumps(cls.get_initial(**override))
../../.pyenv/versions/3.10.8-debug/envs/appdaemon/lib/python3.10/site-packages/tomlkit/api.py:51: in dumps
    data = item(dict(data), _sort_keys=sort_keys)
../../.pyenv/versions/3.10.8-debug/envs/appdaemon/lib/python3.10/site-packages/tomlkit/items.py:181: in item
    val[k] = item(v, _parent=val, _sort_keys=_sort_keys)

ValueError: Invalid type <class 'appdaemon.config.AppConfig.AppDaemon'>

It seems to me that the toml serialization can only handle basic Python types?

mion00 avatar Mar 04 '23 09:03 mion00

The lack of complex types is somewhat intentional to keep the library simple and support importing anything as an environment variable.

Is the goal just to convert yaml from toml? If so, I'd recommend a purpose built tool such as yj.

If you also want to model these in the config, instead of using complex types, I would consider functionality that let you read a config from a specific key prefix in the file. This could work similar to the env_prefix provided by the BaseSettings class.

That would support your use case by allowing you to define multiple GoodConf classes, each with their own key prefix to load from.

ipmb avatar Mar 04 '23 18:03 ipmb

Sorry my bad, I made a typo. I meant I was trying to use both yaml and toml config files as sources, a feature that this library readily provides. My question originates from the fact that GoodConf already loads yaml or toml files with complex structures, such as the following:

class AppConfig(GoodConf):
    class InnerConfig(BaseModel):
        value: str
    inner: InnerConfig
    class Config:
        default_files = ["myproject.yaml", "myproject.toml"]

config = AppConfig()
config.load()

print(config.inner.value)
inner:
  value: "test"
[inner]
a = "test"

I also know that for the env variables the only supported format is a (quite ugly) JSON string, as the documentation of pydantic demonstrates. So my doubt originates from the fact that only the logic implemented in generating the yaml and toml files is lacking this ability, since the rest of the stack seems to be ready for it.

Thanks anyway for you comment. Do you suggest to hook into the customise_sources method to read only the specific key instead of the whole file?

mion00 avatar Mar 04 '23 19:03 mion00