Configuration
Wireup containers can store and inject configuration. This enables self-contained definitions without having to create factories for every injectable.
Loading Configuration¶
Configuration is passed to the container during creation as a dictionary.
import wireup
import os
container = wireup.create_sync_container(
config={
"database_url": os.environ["DB_CONNECTION_STRING"],
"env": os.environ.get("APP_ENV", "production"),
"max_connections": 100,
}
)
Injecting Configuration¶
Primitives (Key-Value)¶
Inject specific values by key using Inject(config="key").
from typing import Annotated
from wireup import injectable, Inject
@injectable
class DatabaseService:
def __init__(
self,
# Injects the value of "database_url" from the config dict
url: Annotated[str, Inject(config="database_url")],
) -> None:
self.url = url
Structured Objects¶
You are not limited to primitives. You can inject entire configuration objects, such as Dataclasses or Pydantic models. This allows you to group related settings and inject only what a service needs.
from dataclasses import dataclass
@dataclass
class DatabaseConfig:
url: str
max_connections: int
container = wireup.create_sync_container(
config={"db_config": DatabaseConfig(url="...", max_connections=10)},
injectables=[...],
)
from pydantic_settings import BaseSettings
class DatabaseSettings(BaseSettings):
url: str
max_connections: int = 10
container = wireup.create_sync_container(
config={"db": DatabaseSettings()}, # Loads from env automatically
injectables=[...],
)
Then inject the configuration object:
import sqlite3
@injectable
class DatabaseService:
def __init__(
self, config: Annotated[DatabaseConfig, Inject(config="db_config")]
) -> None:
self.connection = sqlite3.connect(config.url)
Interpolation¶
You can create dynamic configuration values by interpolating other configuration keys using the ${key} syntax.
# config = {"env": "prod", "host": "localhost", "port": 5432}
@injectable
class FileStorageService:
def __init__(
self,
# Becomes "/tmp/uploads/prod"
upload_path: Annotated[str, Inject(expr="/tmp/uploads/${env}")],
# Becomes "postgresql://localhost:5432/mydb"
db_url: Annotated[
str, Inject(expr="postgresql://${host}:${port}/mydb")
],
) -> None:
self.upload_path = upload_path
Expression results are strings
Configuration expressions always return strings. Non-string configuration values are converted using str() before
interpolation.
Aliasing Configuration Keys¶
Avoid repeating string keys across your codebase by creating type aliases. This also makes refactoring easier if configuration keys change.
# Create an alias for the configuration injection
EnvConfig = Annotated[str, Inject(config="env")]
# Use the alias instead of repeating Inject(config="env")
def list_users(env: EnvConfig) -> None: ...
def get_users(env: EnvConfig) -> None: ...
Next Steps¶
- Lifetimes & Scopes - Control how long objects live.
- Factories - Create complex dependencies and third-party objects.
- Testing - Override configuration values for testing.