Konfigurationssystem mit Validierung, Environment-Substitution, Vererbung und Migration.
from trixy_core.config import (
# Core
ConfigManager,
ServerConfig, ClientConfig, StandaloneConfig, TrainerConfig,
# Environment
EnvironmentSubstitutor, substitute_env,
# Validation
ConfigSchema, ConfigFieldSpec, validate_config,
# Inheritance
ConfigInheritance, deep_merge,
# CLI
CLIConfigMerger, CLIOption,
# Secrets
SecretsProvider, EncryptedSecretsProvider,
EnvironmentSecretsProvider, FileSecretsProvider,
# Migration
ConfigMigration, ConfigMigrator,
)
Lädt und verwaltet Konfigurationen.
from trixy_core.config import ConfigManager
# Manager erstellen
manager = ConfigManager()
# Konfiguration laden
config = manager.load("config/server_config.json")
# Mit Validierung
config = manager.load(
"config/server_config.json",
schema=ServerConfigSchema,
validate=True,
)
# Als Dataclass
server_config: ServerConfig = manager.load_as(
"config/server_config.json",
ServerConfig,
)
# Werte abrufen
port = config.get("server.port", default=2101)
debug = config.get("debug", default=False)
# Dateiüberwachung
manager.watch("config/server_config.json", callback=on_config_changed)
# Konfiguration speichern
manager.save("config/server_config.json", config)
from trixy_core.config import ConfigManager
manager = ConfigManager(auto_reload=True)
@manager.on_change("config/server_config.json")
def handle_config_change(old_config, new_config):
print("Konfiguration geändert")
apply_new_config(new_config)
from trixy_core.config import ServerConfig
@dataclass
class ServerConfig:
host: str = "0.0.0.0"
port: int = 2101
debug: bool = False
# Netzwerk
command_port: int = 2101
audio_in_port: int = 2102
audio_out_port: int = 2103
music_port: int = 2104
# Sicherheit
encryption_key: str = ""
require_registration: bool = True
registration_timeout: int = 60
# Audio
sample_rate: int = 16000
channels: int = 1
chunk_size: int = 1024
# Plugins
plugin_directory: str = "plugins"
enabled_plugins: list[str] = field(default_factory=list)
from trixy_core.config import ClientConfig
@dataclass
class ClientConfig:
server_host: str = ""
server_port: int = 2101
room_id: str = ""
alias: str = ""
# Wakeword
wakeword_model: str = "default"
wakeword_sensitivity: float = 0.5
# Audio
audio_device: str = "default"
sample_rate: int = 16000
Ersetzt Umgebungsvariablen in Konfigurationen.
from trixy_core.config import EnvironmentSubstitutor, substitute_env
# JSON-Konfiguration
config = {
"database": {
"host": "${DB_HOST}",
"port": "${DB_PORT:-5432}",
"password": "${DB_PASSWORD}",
},
"api_key": "${API_KEY:-default_key}",
}
# Substitution durchführen
substitutor = EnvironmentSubstitutor()
result = substitutor.substitute(config)
# Oder einfach
result = substitute_env(config)
# Ergebnis (mit DB_HOST=localhost, DB_PASSWORD=secret)
# {
# "database": {
# "host": "localhost",
# "port": "5432", # Default verwendet
# "password": "secret",
# },
# "api_key": "default_key", # Default verwendet
# }
| Pattern | Beschreibung |
|---|---|
${VAR} |
Pflicht-Variable |
${VAR:-default} |
Mit Default-Wert |
${VAR:?error} |
Fehler wenn nicht gesetzt |
Schema-basierte Konfigurationsvalidierung.
from trixy_core.config import (
ConfigSchema,
ConfigFieldSpec,
ConfigValueType,
validate_config,
ConfigValidationError,
)
# Schema definieren
schema = ConfigSchema(
fields={
"host": ConfigFieldSpec(
type=ConfigValueType.STRING,
required=True,
pattern=r"^[\w.-]+$",
),
"port": ConfigFieldSpec(
type=ConfigValueType.INTEGER,
required=True,
min_value=1,
max_value=65535,
),
"debug": ConfigFieldSpec(
type=ConfigValueType.BOOLEAN,
default=False,
),
"plugins": ConfigFieldSpec(
type=ConfigValueType.LIST,
item_type=ConfigValueType.STRING,
),
}
)
# Validieren
try:
validated = validate_config(config, schema)
except ConfigValidationError as e:
for error in e.errors:
print(f"{error.field}: {error.message}")
from trixy_core.config import schema_from_dataclass
@dataclass
class MyConfig:
name: str
count: int = 10
enabled: bool = True
schema = schema_from_dataclass(MyConfig)
validated = validate_config(config, schema)
Mehrschichtige Konfiguration mit Overrides.
from trixy_core.config import ConfigInheritance, deep_merge
inheritance = ConfigInheritance()
# Basis-Konfiguration
inheritance.add_layer("base", {
"logging": {"level": "INFO"},
"server": {"host": "localhost", "port": 8080},
})
# Environment-Override
inheritance.add_layer("production", {
"logging": {"level": "WARNING"},
"server": {"host": "0.0.0.0"},
})
# Lokale Overrides
inheritance.add_layer("local", {
"debug": True,
})
# Zusammenführen
config = inheritance.merge()
# Ergebnis:
# {
# "logging": {"level": "WARNING"},
# "server": {"host": "0.0.0.0", "port": 8080},
# "debug": True,
# }
from trixy_core.config import ConfigInheritance
inheritance = ConfigInheritance()
# Dateien in Reihenfolge laden
inheritance.load_file("config/base.json")
inheritance.load_file("config/production.json")
inheritance.load_file("config/local.json", optional=True)
config = inheritance.merge()
Konfiguration aus Kommandozeilen-Argumenten.
from trixy_core.config import CLIConfigMerger, CLIOption
merger = CLIConfigMerger()
# Optionen definieren
merger.add_option(CLIOption(
name="--host",
config_path="server.host",
type=str,
))
merger.add_option(CLIOption(
name="--port",
config_path="server.port",
type=int,
))
merger.add_option(CLIOption(
name="--debug",
config_path="debug",
type=bool,
flag=True,
))
# Mit Datei-Konfiguration zusammenführen
import argparse
parser = argparse.ArgumentParser()
args = parser.parse_args()
config = merger.merge(file_config, args)
Sichere Speicherung sensibler Daten.
from trixy_core.config.secrets import EncryptedSecretsProvider
provider = EncryptedSecretsProvider(
key_file="secrets.key",
secrets_file="secrets.enc",
)
# Secret setzen
provider.set("database_password", "super_secret")
provider.set("api_key", "key_12345")
# Secret abrufen
password = provider.get("database_password")
# Als SecretValue (schützt vor versehentlichem Logging)
secret = provider.get_secure("api_key")
print(secret) # Gibt "***" aus
print(secret.reveal()) # Gibt echten Wert aus
from trixy_core.config.secrets import EnvironmentSecretsProvider
provider = EnvironmentSecretsProvider(prefix="TRIXY_")
# Liest TRIXY_DATABASE_PASSWORD aus Umgebung
password = provider.get("database_password")
from trixy_core.config.secrets import FileSecretsProvider
provider = FileSecretsProvider(
directory=Path("/run/secrets"),
encoding="utf-8",
)
# Liest /run/secrets/database_password
password = provider.get("database_password")
Versionierte Migrationen zwischen Config-Versionen.
from trixy_core.config.versioning import (
ConfigMigration,
ConfigMigrator,
MigrationResult,
)
# Migrationen definieren
class MigrateV1ToV2(ConfigMigration):
from_version = "1.0.0"
to_version = "2.0.0"
def migrate(self, config: dict) -> dict:
# Port-Feld umbenannt
if "port" in config:
config["server_port"] = config.pop("port")
# Neues Pflichtfeld
config.setdefault("encryption_enabled", True)
return config
class MigrateV2ToV3(ConfigMigration):
from_version = "2.0.0"
to_version = "3.0.0"
def migrate(self, config: dict) -> dict:
# Strukturänderung
config["server"] = {
"port": config.pop("server_port"),
"host": config.pop("host", "localhost"),
}
return config
# Migrator erstellen
migrator = ConfigMigrator()
migrator.register(MigrateV1ToV2())
migrator.register(MigrateV2ToV3())
# Migration durchführen
old_config = {"version": "1.0.0", "port": 8080}
result = migrator.migrate(old_config, target_version="3.0.0")
print(f"Migriert von {result.from_version} zu {result.to_version}")
print(f"Neue Konfiguration: {result.config}")
from trixy_core.config.versioning import diff_configs, DiffType
old = {"a": 1, "b": {"c": 2}}
new = {"a": 1, "b": {"c": 3, "d": 4}}
diff = diff_configs(old, new)
for entry in diff.entries:
print(f"{entry.type.name}: {entry.path} = {entry.new_value}")
# MODIFIED: b.c = 3
# ADDED: b.d = 4