Basic Usage
This page provides basic examples of using objectstate.
Simple Configuration
The most basic usage of objectstate:
from dataclasses import dataclass
from objectstate import (
set_base_config_type,
LazyDataclassFactory,
config_context,
)
@dataclass
class AppConfig:
api_key: str = "default-key"
timeout: int = 30
retries: int = 3
# Setup
set_base_config_type(AppConfig)
LazyAppConfig = LazyDataclassFactory.make_lazy_simple(AppConfig)
# Create configuration
config = AppConfig(
api_key="prod-key-123",
timeout=60
)
# Use with context
with config_context(config):
lazy = LazyAppConfig()
print(lazy.api_key) # "prod-key-123"
print(lazy.timeout) # 60
print(lazy.retries) # 3 (default)
Multiple Configurations
Managing multiple configuration types:
from dataclasses import dataclass
from objectstate import LazyDataclassFactory, config_context
@dataclass
class DatabaseConfig:
host: str = "localhost"
port: int = 5432
database: str = "myapp"
@dataclass
class CacheConfig:
backend: str = "redis"
ttl: int = 300
max_size: int = 1000
# Create lazy versions
LazyDB = LazyDataclassFactory.make_lazy_simple(DatabaseConfig)
LazyCache = LazyDataclassFactory.make_lazy_simple(CacheConfig)
# Setup configurations
db_config = DatabaseConfig(
host="prod.db.internal",
port=5433,
database="production"
)
cache_config = CacheConfig(
backend="memcached",
ttl=600
)
# Use both in context
with config_context(db_config):
with config_context(cache_config):
db = LazyDB()
cache = LazyCache()
print(f"Connecting to {db.host}:{db.port}/{db.database}")
print(f"Cache: {cache.backend}, TTL: {cache.ttl}s")
Function Integration
Using lazy configs with functions:
from dataclasses import dataclass
from objectstate import LazyDataclassFactory, config_context
@dataclass
class ProcessConfig:
batch_size: int = 100
parallel: bool = False
log_level: str = "INFO"
LazyProcess = LazyDataclassFactory.make_lazy_simple(ProcessConfig)
def process_items(items: list, config: LazyProcess):
"""Process items using configuration."""
print(f"Processing {len(items)} items")
print(f"Batch size: {config.batch_size}")
print(f"Parallel: {config.parallel}")
print(f"Log level: {config.log_level}")
# Process in batches
for i in range(0, len(items), config.batch_size):
batch = items[i:i + config.batch_size]
print(f"Processing batch of {len(batch)} items")
# Use it
config = ProcessConfig(batch_size=50, parallel=True)
with config_context(config):
items = list(range(250))
lazy_cfg = LazyProcess()
process_items(items, lazy_cfg)
Overriding Values
Explicitly override context values:
from dataclasses import dataclass
from objectstate import LazyDataclassFactory, config_context
@dataclass
class ServerConfig:
host: str = "0.0.0.0"
port: int = 8000
workers: int = 4
LazyServer = LazyDataclassFactory.make_lazy_simple(ServerConfig)
# Context provides defaults
context_config = ServerConfig(
host="prod.server.com",
port=443,
workers=8
)
with config_context(context_config):
# Use all from context
server1 = LazyServer()
print(f"Server 1: {server1.host}:{server1.port}, workers={server1.workers}")
# Output: Server 1: prod.server.com:443, workers=8
# Override specific values
server2 = LazyServer(port=8443)
print(f"Server 2: {server2.host}:{server2.port}, workers={server2.workers}")
# Output: Server 2: prod.server.com:8443, workers=8
# Override multiple values
server3 = LazyServer(host="localhost", port=8080, workers=1)
print(f"Server 3: {server3.host}:{server3.port}, workers={server3.workers}")
# Output: Server 3: localhost:8080, workers=1
Automatic Field Injection with Decorators
Using the decorator pattern for automatic field injection and lazy class generation:
from dataclasses import dataclass
from objectstate import auto_create_decorator, ensure_global_config_context
# Create global config with auto_create_decorator
@auto_create_decorator
@dataclass
class GlobalPipelineConfig:
num_workers: int = 4
verbose: bool = False
# This automatically creates:
# 1. A decorator named `global_pipeline_config`
# 2. A lazy class named `PipelineConfig`
# Use the decorator on other config classes
@global_pipeline_config
@dataclass
class DatabaseConfig:
host: str = "localhost"
port: int = 5432
pool_size: int = 10
@global_pipeline_config
@dataclass
class CacheConfig:
backend: str = "redis"
ttl: int = 300
# After module loading, GlobalPipelineConfig automatically has:
# - database_config: DatabaseConfig = DatabaseConfig()
# - cache_config: CacheConfig = CacheConfig()
# And lazy classes are created: LazyDatabaseConfig, LazyCacheConfig
# Create and set up global config instance
global_config = GlobalPipelineConfig(
num_workers=8,
verbose=True
)
# REQUIRED: Establish global context for lazy resolution
ensure_global_config_context(GlobalPipelineConfig, global_config)
# Now you can access injected configs
print(f"Database: {global_config.database_config.host}:{global_config.database_config.port}")
print(f"Cache: {global_config.cache_config.backend}, TTL={global_config.cache_config.ttl}")
Key Benefits:
Automatic injection: Decorated configs become fields in the global config
Lazy class generation: Lazy versions are automatically created
Modular structure: Each component has its own config class
Type-safe: Full IDE support and type checking
Nested Dataclass Auto-Lazification
The framework automatically converts nested dataclass fields to lazy versions:
from dataclasses import dataclass
from objectstate import LazyDataclassFactory, config_context
@dataclass
class LoggingConfig:
level: str = "INFO"
format: str = "json"
@dataclass
class AppConfig:
app_name: str = "MyApp"
logging: LoggingConfig = LoggingConfig()
port: int = 8000
# Create lazy version - nested LoggingConfig is automatically lazified
LazyAppConfig = LazyDataclassFactory.make_lazy_simple(AppConfig)
# Use it
config = AppConfig(
app_name="ProductionApp",
logging=LoggingConfig(level="WARNING", format="text"),
port=443
)
with config_context(config):
lazy_app = LazyAppConfig()
# Access nested config - it's automatically lazy
print(f"App: {lazy_app.app_name}")
print(f"Log level: {lazy_app.logging.level}")
print(f"Log format: {lazy_app.logging.format}")
print(f"Port: {lazy_app.port}")
Benefits:
No need to manually create
LazyLoggingConfigWorks recursively for deeply nested configs
Preserves all field properties and metadata