GPConfigurable Class¶
GPConfigurable is the base class for all configurable objects. By inheriting from this class, you can create objects that are instantiated from GPConfig configurations.
Import¶
Class Definition¶
class GPConfigurable:
def __init__(self, config: "GPConfig") -> None:
"""Initialize the configurable object from its config."""
self._config = config
@property
def config(self) -> "GPConfig":
"""Access the configuration object."""
return self._config
Usage Pattern¶
Basic Usage¶
from typing import ClassVar
from gpconfig import GPConfig, GPConfigurable, GPConfigManager
# 1. Define config class
class DatabaseConfig(GPConfig):
cfg_class_name: ClassVar[str] = "DatabaseConfig"
host: str
port: int = 5432
username: str
password: str
database: str
# 2. Define configurable object class
class Database(GPConfigurable):
"""Database connection object"""
def __init__(self, config: DatabaseConfig) -> None:
super().__init__(config)
self.host = config.host
self.port = config.port
self.username = config.username
self.password = config.password
self.database = config.database
self._connection = None
def connect(self):
"""Establish database connection"""
print(f"Connecting to {self.host}:{self.port}/{self.database}")
# Actual connection logic...
def close(self):
"""Close connection"""
if self._connection:
self._connection.close()
Register and Create Objects¶
# 3. Initialize manager
manager = GPConfigManager("myapp")
# 4. Register config class and configurable class (separately)
GPConfigManager.register_config_class(DatabaseConfig)
GPConfigManager.register_configurable_class(Database)
# 5. Create object instance from config
db = manager.get_object("database")
# Use the object
db.connect()
Config file (database.yaml):
cfg_class_name: "DatabaseConfig"
configured_class_name: "Database"
host: localhost
port: 5432
username: admin
password: secret
database: myapp
config Property¶
Access the original config object through the config property:
class Cache(GPConfigurable):
def __init__(self, config: "CacheConfig") -> None:
super().__init__(config)
self.host = config.host
self.port = config.port
def reconnect(self):
# Access config through config property
print(f"Reconnecting to {self.config.host}:{self.config.port}")
cache = manager.get_object("cache")
print(cache.config.ttl) # Access field from config
Complete Example¶
Multiple Configurable Objects¶
from typing import ClassVar
from gpconfig import GPConfig, GPConfigurable, GPConfigManager
# Config class
class LLMConfig(GPConfig):
cfg_class_name: ClassVar[str] = "LLMConfig"
api_key: str
model: str
temperature: float = 0.7
max_tokens: int = 4096
# Configurable object
class LLMProvider(GPConfigurable):
"""LLM Provider"""
def __init__(self, config: LLMConfig) -> None:
super().__init__(config)
self.api_key = config.api_key
self.model = config.model
self.temperature = config.temperature
self.max_tokens = config.max_tokens
def generate(self, prompt: str) -> str:
"""Generate text"""
print(f"Using model: {self.model}")
print(f"Temperature: {self.temperature}")
# Actual LLM API call...
return f"Response to: {prompt}"
# Initialize
manager = GPConfigManager("myapp")
# Register config class and configurable class separately
GPConfigManager.register_config_class(LLMConfig)
GPConfigManager.register_configurable_class(LLMProvider)
# Create different objects using different configs
openai = manager.get_object("llm.openai")
anthropic = manager.get_object("llm.anthropic")
print(openai.model) # gpt-4
print(anthropic.model) # claude-3-opus
YAML config files:
# llm/openai.yaml
cfg_class_name: "LLMConfig"
configured_class_name: "LLMProvider"
api_key: sk-xxx
model: gpt-4
temperature: 0.7
max_tokens: 4096
# llm/anthropic.yaml
cfg_class_name: "LLMConfig"
configured_class_name: "LLMProvider"
api_key: sk-yyy
model: claude-3-opus
temperature: 0.8
max_tokens: 8192
Accessing Config Metadata¶
class Service(GPConfigurable):
def __init__(self, config: "ServiceConfig") -> None:
super().__init__(config)
self.name = config.name # Config name
self.url = config.url
def info(self):
return {
"name": self.name,
"config_file": str(self.config.cfg_file_path),
"url": self.url
}
service = manager.get_object("api_service")
print(service.info())
# {'name': 'api_service', 'config_file': '/path/to/api_service.yaml', 'url': '...'}
Notes¶
Each Call Creates New Instance¶
get_object() creates a new object instance on each call:
db1 = manager.get_object("database")
db2 = manager.get_object("database")
print(db1 is db2) # False - different instances
Must Call super().init()¶
Subclasses must call the parent class's __init__ method:
class MyConfigurable(GPConfigurable):
def __init__(self, config: MyConfig) -> None:
super().__init__(config) # Must call
# Initialization logic...
Type Hints¶
It's recommended to add type hints for the config parameter for better IDE support: