Usage

Create your models as you did before. Then pass an instance of the model to the easyconfig function. It will create a mutable object from the model that holds the same values.

Easyconfig also provides some mixin classes, so you can have type hints for the file load functions. These mixins are not required, they are just there to provide type hints in the IDE.

For convenience reasons you can also import AppBaseModel and BaseModel from easyconfig so you don’t have to inherit from the mixins yourself.

Simple example

from pydantic import BaseModel
from easyconfig import AppConfigMixin, create_app_config


class MySimpleAppConfig(BaseModel, AppConfigMixin):
    retries: int = 5
    url: str = 'localhost'
    port: int = 443


# Create a global variable which then can be used throughout your code
CONFIG = create_app_config(MySimpleAppConfig())

# Use with type hints and auto complete
CONFIG.port

# Load configuration file from disk.
# If the file does not exist it will be created
# Loading will also change all values of CONFIG accordingly
CONFIG.load_config_file('/my/configuration/file.yml')

Generated yaml file

retries: 5
url: localhost
port: 443

Nested example

Nested example with the convenience base classes from easyconfig.

from pydantic import Field
from easyconfig import AppBaseModel, BaseModel, create_app_config


class HttpConfig(BaseModel):
    retries: int = 5
    url: str = 'localhost'
    port: int = 443


class MySimpleAppConfig(AppBaseModel):
    run_at: int = Field(12, alias='run at')  # use alias to load from/create a different key
    http: HttpConfig = HttpConfig()


CONFIG = create_app_config(MySimpleAppConfig())
CONFIG.load_config_file('/my/configuration/file.yml')

Generated yaml file

run at: 12
http:
  retries: 5
  url: localhost
  port: 443

Description and comments

It’s possible to specify a description through the pydantic Field. The description will be created as a comment in the .yml file. Note that the comments will be aligned properly

from pydantic import Field
from easyconfig import AppBaseModel, create_app_config


class MySimpleAppConfig(AppBaseModel):
    retries: int = Field(5, description='Amount of retries on error')
    url: str = Field('localhost', description='Url used for connection')
    port: int = 443


CONFIG = create_app_config(MySimpleAppConfig())
CONFIG.load_config_file('/my/configuration/file.yml')

Generated yaml file

retries: 5      # Amount of retries on error
url: localhost  # Url used for connection
port: 443

Expansion and docker secrets

It’s possible to use environment variable or files for expansion. To expand an environment variable or file use ${NAME} or ${NAME:DEFAULT} to specify an additional default if the value under NAME is not set. To load the content from a file, e.g. a docker secret specify an absolute file name.

Environment variables:

MY_USER =USER_NAME
MY_GROUP=USER: ${MY_USER}, GROUP: GROUP_NAME
ENV_{_SIGN = CURLY_OPEN_WORKS
ENV_}_SIGN = CURLY_CLOSE_WORKS

yaml file

env_var: "${MY_USER}"
env_var_recursive: "${MY_GROUP}"
env_var_not_found: Does not exist -> "${INVALID_NAME}"
env_var_default: Does not exist -> "${INVALID_NAME:DEFAULT_VALUE}"
file: "${/my_file/path.txt}"
escaped: |
    Brackets {} or $ signs can be used as expected.
    Use $${BLA} to escape the whole expansion.
    Use $} to escape the closing bracket, e.g. use "${ENV_$}_SIGN}" for "ENV_}_SIGN"
    The { does not need to be escaped, e.g. use "${ENV_{_SIGN}" for "ENV_{_SIGN"

After expansion

env_var: USER_NAME
env_var_recursive: 'USER: USER_NAME, GROUP: GROUP_NAME'
env_var_not_found: Does not exist -> ""
env_var_default: Does not exist -> "DEFAULT_VALUE"
file: <SECRET_CONTENT_FROM_FILE>
escaped: |
  Brackets {} or $ signs can be used as expected.
  Use ${BLA} to escape the whole expansion.
  Use $} to escape the closing bracket, e.g. use "CURLY_CLOSE_WORKS" for "ENV_}_SIGN"
  The { does not need to be escaped, e.g. use "CURLY_OPEN_WORKS" for "ENV_{_SIGN"

Callbacks

It’s possible to register callbacks that will get executed when a value changes or when the configuration gets loaded for the first time. This is especially useful feature if the application allows dynamic reloading of the configuration file (e.g. through a file watcher).

from easyconfig import AppBaseModel, create_app_config

class MySimpleAppConfig(AppBaseModel):
    retries: int = 5
    url: str = 'localhost'
    port: int = 443

# A function that does the setup
def setup_http():
    # some internal function
    create_my_http_client(CONFIG.url, CONFIG.port)

CONFIG = create_app_config(MySimpleAppConfig())

# setup_http will be automatically called if a value changes in the MyAppSimpleConfig
# during a subsequent call to CONFIG.load_file() or
# when the config gets loaded for the first time
sub = CONFIG.subscribe_for_changes(setup_http)

# It's possible to cancel the subscription again
sub.cancel()

# This will trigger the callback
CONFIG.load_config_file('/my/configuration/file.yml')