Examples

1. Basic Configuration

This example demonstrates the simplest usage of classconfig with basic types.

from classconfig import ConfigurableMixin, ConfigurableValue, Config

class ServerConfig(ConfigurableMixin):
    host: str = ConfigurableValue(desc="Host address", user_default="localhost")
    port: int = ConfigurableValue(desc="Port number", user_default=8080)
    debug: bool = ConfigurableValue(desc="Debug mode", user_default=False)

# Generate config
Config(ServerConfig).save("server_config.yaml")

# Load config
# loaded_config = Config(ServerConfig).load("server_config.yaml")
# server = ConfigurableFactory(ServerConfig).create(loaded_config)

2. Nested Configuration

You can nest configurable objects using ConfigurableFactory.

from classconfig import ConfigurableMixin, ConfigurableValue, ConfigurableFactory

class DatabaseConfig(ConfigurableMixin):
    host: str = ConfigurableValue(desc="DB Host")
    port: int = ConfigurableValue(desc="DB Port", user_default=5432)

class AppConfig(ConfigurableMixin):
    name: str = ConfigurableValue(desc="App Name")
    database: DatabaseConfig = ConfigurableFactory(DatabaseConfig, desc="Database Settings")

3. Polymorphism with Subclass Factory

Use ConfigurableSubclassFactory to allow selecting a specific subclass implementation via configuration.

from classconfig import ConfigurableMixin, ConfigurableValue, ConfigurableSubclassFactory
from abc import ABC, abstractmethod

class Storage(ConfigurableMixin, ABC):
    @abstractmethod
    def save(self, data):
        pass

class DiskStorage(Storage):
    path: str = ConfigurableValue(desc="Storage path")
    def save(self, data): print(f"Saving to disk at {self.path}")

class S3Storage(Storage):
    bucket: str = ConfigurableValue(desc="S3 Bucket")
    def save(self, data): print(f"Saving to S3 bucket {self.bucket}")

class Service(ConfigurableMixin):
    storage: Storage = ConfigurableSubclassFactory(Storage, desc="Storage backend")

# In YAML, you specify the class name:
# storage:
#   cls: S3Storage
#   config:
#     bucket: my-bucket

4. List of Polymorphic Objects

Use ListOfConfigurableSubclassFactoryAttributes to configure a list of objects where each can be a different subclass.

from classconfig import ConfigurableMixin, ListOfConfigurableSubclassFactoryAttributes, ConfigurableSubclassFactory
# Assuming Storage, DiskStorage, S3Storage from Example 3

class BackupService(ConfigurableMixin):
    targets: list[Storage] = ListOfConfigurableSubclassFactoryAttributes(
        ConfigurableSubclassFactory(Storage),
        desc="List of backup targets"
    )

# In YAML:
# targets:
#   - cls: DiskStorage
#     config:
#       path: /backup
#   - cls: S3Storage
#     config:
#       bucket: backup-bucket

5. Dataclasses

You can use Python dataclasses with ConfigurableDataclassMixin.

from dataclasses import dataclass, field
from classconfig.classes import ConfigurableDataclassMixin

@dataclass
class UserConfig(ConfigurableDataclassMixin):
    username: str = field(metadata={"desc": "Username"})
    age: int = field(default=18, metadata={"desc": "Age"})

6. Validation

Ensure configuration values meet specific criteria using validators.

from classconfig import ConfigurableMixin, ConfigurableValue
from classconfig.validators import MinValueIntegerValidator, StringValidator

class RetryConfig(ConfigurableMixin):
    count: int = ConfigurableValue(
        desc="Retry count",
        validator=MinValueIntegerValidator(0)
    )
    strategy: str = ConfigurableValue(
        desc="Retry strategy",
        validator=StringValidator()
    )

7. Transformation (Enums)

Automatically convert string inputs to Enum members.

from classconfig import ConfigurableMixin, ConfigurableValue
from classconfig.transforms import EnumTransformer
from enum import Enum

class Color(Enum):
    RED = "RED"
    GREEN = "GREEN"
    BLUE = "BLUE"

class ThemeConfig(ConfigurableMixin):
    primary_color: Color = ConfigurableValue(
        desc="Primary color",
        transform=EnumTransformer(Color)
    )

8. Relative Paths

Resolve paths relative to the configuration file location.

from classconfig import ConfigurableMixin, ConfigurableValue
from classconfig.transforms import RelativePathTransformer

class FileConfig(ConfigurableMixin):
    # Path will be resolved relative to the config file's directory
    input_file: str = ConfigurableValue(
        desc="Input file path",
        transform=RelativePathTransformer()
    )

9. Delayed Initialization

Delay the creation of objects until needed.

from classconfig import ConfigurableMixin, ConfigurableFactory

class HeavyResource(ConfigurableMixin):
    def __init__(self):
        print("Heavy resource initialized")

class App(ConfigurableMixin):
    # Resource won't be created immediately upon config loading
    resource = ConfigurableFactory(HeavyResource, delay_init=True)

# When loaded:
# app = ...
# resource_factory = app.resource
# resource_instance = resource_factory.create()

10. Inheritance

Configurable classes can inherit from other configurable classes.

from classconfig import ConfigurableMixin, ConfigurableValue

class BaseConfig(ConfigurableMixin):
    name: str = ConfigurableValue(desc="Name")

class ExtendedConfig(BaseConfig):
    # Inherits 'name' from BaseConfig
    version: str = ConfigurableValue(desc="Version")

# ExtendedConfig will have both 'name' and 'version' configurable attributes.

11. Omit Attributes in Factory

You can exclude specific attributes from being configurable when using ConfigurableFactory.

from classconfig import ConfigurableMixin, ConfigurableValue, ConfigurableFactory

class InternalConfig(ConfigurableMixin):
    public_param: str = ConfigurableValue(desc="Public parameter")
    secret_param: str = ConfigurableValue(desc="Secret parameter")

class AppConfig(ConfigurableMixin):
    # 'secret_param' will not be exposed in the configuration file for 'internal'
    internal: InternalConfig = ConfigurableFactory(
        InternalConfig,
        desc="Internal settings",
        omit={"secret_param": {}}
    )

12. Overriding User Defaults

You can override the default values of the nested class when defining the factory.

from classconfig import ConfigurableMixin, ConfigurableValue, ConfigurableFactory

class Server(ConfigurableMixin):
    port: int = ConfigurableValue(desc="Port", user_default=8080)

class AppConfig(ConfigurableMixin):
    # Override default port to 9090 for this specific usage
    server: Server = ConfigurableFactory(
        Server,
        desc="Server settings",
        file_override_user_defaults={"port": 9090}
    )

13. Creatable Mixin

CreatableMixin allows you to create an instance directly from a configuration dictionary or file, without explicitly using ConfigurableFactory.

from classconfig import ConfigurableMixin, ConfigurableValue, CreatableMixin

class MyService(ConfigurableMixin, CreatableMixin):
    name: str = ConfigurableValue(desc="Service Name")

# Create directly from dict
service = MyService.create({"name": "My Service"})
print(service.name)

# Create directly from file
# service = MyService.create("config.yaml")