Goodconf

repository·main·Indexed 41 days ago

https://github.com/lincolnloop/goodconf

A thin wrapper over Pydantic's settings management for loading configuration variables from environment variables or JSON, YAML, and TOML files. It allows developers to define configuration via Python classes, generate initial configuration templates, and create Markdown documentation for configuration parameters.

Tokens
2.3K
Snippets
10
Records
11
Agent score
50%

What's inside Goodconf

  1. Integrate Goodconf with Django

    main

    Goodconf provides a helper to integrate with Django's management commands.

    1. Install the Django extra: pip install goodconf[django].
    2. Update your manage.py to use config.django_manage(). This monkey-patches Django's management commands to accept a --config argument.
    # myproject/manage.py
    from myproject.conf import config
    
    if __name__ == '__main__':
        config.django_manage()
  2. Install Goodconf

    main

    Install the core package via pip. If you need to parse or generate YAML or TOML configuration files, install the corresponding extras.

    On Python 3.11+, the [toml] extra is only required for generating TOML files, as parsing is supported natively.

    pip install goodconf
    pip install goodconf[yaml]
    pip install goodconf[toml]
  3. Quick Start: Define and use configuration

    main

    To use Goodconf, subclass GoodConf to define your schema. You can use standard Python types or Pydantic types. Use the Field function to add metadata like initial values (callables used when generating config files) or description (used as headers in generated files).

    1. Define your config in a conf.py file.
    2. Use config.load() to trigger the loading process.
    3. Access values via the config instance.

    Environment variables always take precedence over values in configuration files.

    import base64
    import os
    from goodconf import GoodConf, Field
    from pydantic import PostgresDsn
    
    class AppConfig(GoodConf):
        """Configuration for My App"""
        DEBUG: bool
        DATABASE_URL: PostgresDsn = "postgres://localhost:5432/mydb"
        SECRET_KEY: str = Field(
            initial=lambda: base64.b64encode(os.urandom(60)).decode(),
            description="Used for cryptographic signing."
        )
    
        model_config = {"default_files": ["/etc/myproject/myproject.yaml", "myproject.yaml"]}
    
    config = AppConfig()
    
    # In your settings.py or application entrypoint:
    config.load()
    DEBUG = config.DEBUG
  4. Configure GoodConf via model_config

    main

    Subclassed GoodConf objects can include a model_config dictionary to control loading behavior:

    • file_env_var: The name of an environment variable that holds the path to the configuration file to load.
    • default_files: A list of file paths to try loading in order if no file is explicitly passed to the load() method.
  5. Generate initial configuration files

    main

    You can generate a YAML configuration file based on your GoodConf definition. This is useful for providing a template for developers or deployment systems.

    You can run this via a one-liner in the terminal:

    python -c "import myproject; print(myproject.conf.config.generate_yaml(DEBUG=True))" > myproject.yaml
  6. Define configuration fields with Field()

    main

    Use the Field function to enhance configuration attributes. One specialized argument is:

    • initial: A callable used to generate an initial value specifically when generating a configuration file (e.g., generating a random secret key).
    from goodconf import Field
    
    # Example: generating a random string for a new config file
    SECRET_KEY: str = Field(initial=lambda: "generated_secret")
  7. Configure file loading with GoodConfConfigDict

    main

    You can customize how GoodConf searches for configuration files by setting model_config using GoodConfConfigDict. This allows you to define:

    • file_env_var: An environment variable name that holds the path to the configuration file. If set, GoodConf will look for the file at this path.
    • default_files: A list of file paths to check in order if no specific configuration file is provided via config_file or the environment variable.
    from goodconf import GoodConf, GoodConfConfigDict
    
    class MySettings(GoodConf):
        model_config = GoodConfConfigDict(
            file_env_var="MYAPP_CONFIG_PATH",
            default_files=["config/settings.yaml", "config/settings.json"]
        )
        api_key: str
  8. Generate configuration templates (YAML, JSON, TOML)

    main

    GoodConf provides class methods to generate initial configuration files based on your schema. These methods respect field descriptions (adding them as comments) and use the initial callable from Field to populate values.

    • generate_yaml(**override): Returns a YAML string. If the class has a docstring, it is added as a header comment.
    • generate_json(**override): Returns a JSON string.
    • generate_toml(**override): Returns a TOML string. If the class has a docstring, it is added as a comment.

    **override** allows you to pass specific values to override the initial values in the generated output.

    from myapp.conf import MySettings
    
    # Generate a YAML template for the user
    yaml_template = MySettings.generate_yaml()
    print(yaml_template)
    
    # Generate a TOML template with a specific override
    toml_template = MySettings.generate_toml(port=9000)
    print(toml_template)
  9. Use Field for advanced field definitions and initial values

    main

    The Field function is a wrapper around pydantic.Field. It supports all standard Pydantic field arguments (like default, description, etc.) but adds a goodconf-specific initial argument.

    The initial argument accepts a callable that returns a placeholder value. This value is used when generating configuration templates (YAML, JSON, TOML) via the generate_* methods, ensuring that template files contain useful starting values rather than just nulls or defaults.

    from goodconf import GoodConf, Field
    
    class MySettings(GoodConf):
        # Use 'initial' to provide a template value that differs from the runtime default
        db_password: str = Field(default="", initial=lambda: "CHANGE_ME")
        timeout: int = Field(default=30, description="Connection timeout in seconds")
  10. Generate Markdown documentation for configuration

    main

    Use generate_markdown() to create a human-readable list of all configuration parameters. This is useful for generating documentation for end-users or DevOps teams. It includes:

    • The class docstring as a header.
    • Field names (marked as _REQUIRED_ if they have no default).
    • Field descriptions.
    • Field types.
    • Default values.
    from myapp.conf import MySettings
    
    print(MySettings.generate_markdown())
  11. Define configuration schemas with GoodConf

    main

    To define a configuration schema, subclass GoodConf. GoodConf inherits from Pydantic's BaseSettings, allowing you to use type annotations to define your configuration fields. You can control how configuration is loaded using model_config of type GoodConfConfigDict.

    When instantiating GoodConf, you can pass load=True to immediately load from a configuration file, or provide a config_file path. If load=False (the default), you can call the .load(filename) method later to trigger the loading process.

    from goodconf import GoodConf, Field
    
    class MySettings(GoodConf):
        """My application configuration settings."""
        api_key: str
        port: int = 8080
        debug: bool = False
    
    # Load immediately from a file
    settings = MySettings(load=True, config_file="config.yaml")
    
    # Or load later
    settings = MySettings()
    settings.load("config.yaml")