Skip to main content

Reserved placeholder for pyw-config (configuration utilities for pythonWoods suite)

Project description

pyw-config ⚙️

PyPI CI License

Configuration management utilities for the pythonWoods ecosystem.

Overview

pyw-config fornisce un sistema di gestione configurazioni type-safe e flessibile, con supporto per multipli backend (file, environment variables, remote sources) e validazione automatica tramite Pydantic.

Philosophy

  • Type-safe configs – Pydantic models per zero errori di configurazione
  • Multiple sources – YAML, JSON, TOML, .env, environment variables
  • Hierarchical merging – override intelligente di configurazioni
  • Environment-aware – profili per dev/staging/prod
  • Validation-first – errori chiari e actionable per configurazioni invalide

Installation

pip install pyw-config

Per backend aggiuntivi:

pip install pyw-config[yaml]      # + PyYAML per file YAML
pip install pyw-config[toml]      # + tomli/tomllib per TOML
pip install pyw-config[vault]     # + hvac per HashiCorp Vault
pip install pyw-config[remote]    # + requests per config remote
pip install pyw-config[full]      # tutti i backend

Quick Start

Basic Configuration

from pyw.config import BaseConfig, Field
from pyw.config.sources import from_file, from_env

class DatabaseConfig(BaseConfig):
    host: str = Field(default="localhost")
    port: int = Field(default=5432, ge=1, le=65535)
    username: str
    password: str = Field(..., min_length=8)
    database: str
    ssl_enabled: bool = Field(default=True)

class AppConfig(BaseConfig):
    debug: bool = Field(default=False)
    secret_key: str = Field(..., min_length=32)
    db: DatabaseConfig
    api_timeout: float = Field(default=30.0, gt=0)

# Carica da file + environment
config = AppConfig.from_sources(
    from_file("config.yaml"),
    from_env(prefix="MYAPP_")
)

print(f"Connecting to {config.db.host}:{config.db.port}")

Configuration Files

config.yaml:

debug: false
secret_key: "your-super-secret-key-here-min-32-chars"
db:
  host: "prod-db.example.com"
  username: "myapp"
  database: "production"
  ssl_enabled: true
api_timeout: 60.0

Environment Variables:

export MYAPP_DEBUG=true
export MYAPP_DB_PASSWORD=secure_password_123
export MYAPP_DB_PORT=5433

Features

🔄 Multiple Configuration Sources

from pyw.config.sources import (
    from_file, from_env, from_dict, 
    from_vault, from_url
)

config = AppConfig.from_sources(
    # 1. File di base
    from_file("config.yaml"),
    
    # 2. Override per environment
    from_file(f"config.{env}.yaml", optional=True),
    
    # 3. Secrets da Vault
    from_vault("secret/myapp", optional=True),
    
    # 4. Environment variables (priorità massima)
    from_env(prefix="MYAPP_"),
    
    # 5. Config remota
    from_url("https://config.myapp.com/api/config", optional=True)
)

🌍 Environment Profiles

from pyw.config import ConfigProfile

class AppConfig(BaseConfig):
    class Meta:
        profiles = {
            "development": {
                "debug": True,
                "db.host": "localhost",
                "api_timeout": 5.0
            },
            "production": {
                "debug": False,
                "db.ssl_enabled": True,
                "api_timeout": 30.0
            }
        }

# Carica profilo automaticamente da ENV
config = AppConfig.load_profile()  # Legge ENVIRONMENT=production

# Oppure esplicitamente
config = AppConfig.load_profile("development")

🔒 Secrets Management

from pyw.config import SecretStr, SecretBytes
from pyw.config.secrets import from_keyring, from_1password

class Config(BaseConfig):
    # Secrets non loggati/serializzati
    api_key: SecretStr
    private_key: SecretBytes
    
    # Caricamento da secret manager
    class Meta:
        secret_sources = [
            from_keyring("myapp"),
            from_1password("myapp-vault")
        ]

# I secrets sono automaticamente mascherati
print(config.api_key)  # → SecretStr('**********')
print(config.api_key.get_secret_value())  # → valore reale

🔄 Dynamic Configuration

from pyw.config import WatchableConfig
import asyncio

class AppConfig(WatchableConfig):
    feature_flags: dict[str, bool] = Field(default_factory=dict)
    rate_limit: int = 100

# Reload automatico su cambio file
config = AppConfig.from_file("config.yaml", watch=True)

@config.on_change
async def config_changed(old_config, new_config):
    if old_config.rate_limit != new_config.rate_limit:
        await update_rate_limiter(new_config.rate_limit)

# Avvia watching
await config.start_watching()

📊 Configuration Validation

from pyw.config import validator, root_validator
from typing import Optional

class DatabaseConfig(BaseConfig):
    host: str
    port: int = Field(ge=1, le=65535)
    replica_hosts: Optional[list[str]] = None
    
    @validator('host')
    def validate_host(cls, v):
        if not v or v == 'localhost':
            return v
        # Valida formato hostname/IP
        import socket
        try:
            socket.gethostbyname(v)
            return v
        except socket.gaierror:
            raise ValueError(f"Invalid hostname: {v}")
    
    @root_validator
    def validate_replicas(cls, values):
        if values.get('replica_hosts'):
            main_host = values.get('host')
            if main_host in values['replica_hosts']:
                raise ValueError("Main host cannot be in replica list")
        return values

🧪 Testing Support

from pyw.config.testing import temporary_config, mock_env

class TestApp:
    def test_with_temp_config(self):
        with temporary_config(AppConfig, {"debug": True}):
            config = AppConfig.load()
            assert config.debug is True
    
    def test_with_mock_env(self):
        with mock_env(MYAPP_DEBUG="false"):
            config = AppConfig.from_env(prefix="MYAPP_")
            assert config.debug is False

Advanced Usage

Custom Configuration Sources

from pyw.config.sources import ConfigSource
import redis

class RedisConfigSource(ConfigSource):
    def __init__(self, redis_client, key_prefix="config:"):
        self.redis = redis_client
        self.prefix = key_prefix
    
    def load(self) -> dict:
        keys = self.redis.keys(f"{self.prefix}*")
        config = {}
        for key in keys:
            config_key = key.decode().replace(self.prefix, "")
            config[config_key] = self.redis.get(key).decode()
        return config

# Utilizzo
redis_client = redis.Redis()
config = AppConfig.from_sources(
    RedisConfigSource(redis_client),
    from_env(prefix="MYAPP_")
)

Configuration Schemas

from pyw.config import ConfigSchema, generate_schema

# Genera JSON Schema
schema = generate_schema(AppConfig)
print(schema)

# Genera esempio di configurazione
example = AppConfig.generate_example()
with open("config.example.yaml", "w") as f:
    yaml.dump(example, f)

# Validazione esterna
from pyw.config.validation import validate_file

errors = validate_file("config.yaml", AppConfig)
if errors:
    for error in errors:
        print(f"❌ {error.location}: {error.message}")

Configuration Migrations

from pyw.config.migrations import ConfigMigration

class Migration001(ConfigMigration):
    """Rename db_host to database.host"""
    version = "0.0.1"
    
    def migrate(self, config: dict) -> dict:
        if "db_host" in config:
            config.setdefault("database", {})
            config["database"]["host"] = config.pop("db_host")
        return config

# Auto-apply migrations
config = AppConfig.from_file("old-config.yaml", 
                           migrations=[Migration001()])

Integration Examples

FastAPI Integration

from fastapi import FastAPI, Depends
from pyw.config import inject_config

app = FastAPI()

@app.get("/status")
def get_status(config: AppConfig = Depends(inject_config(AppConfig))):
    return {
        "debug": config.debug,
        "database_host": config.db.host
    }

Django Integration

# settings.py
from pyw.config import django_settings

class DjangoConfig(BaseConfig):
    SECRET_KEY: str
    DEBUG: bool = False
    DATABASES: dict
    ALLOWED_HOSTS: list[str] = Field(default_factory=list)

# Auto-populate Django settings
config = DjangoConfig.from_sources(
    from_file("django.yaml"),
    from_env(prefix="DJANGO_")
)

globals().update(django_settings(config))

CLI Integration

# Valida configurazione
pyw-config validate config.yaml --schema=myapp.config:AppConfig

# Genera esempio
pyw-config generate-example myapp.config:AppConfig > config.example.yaml

# Merge configurazioni
pyw-config merge base.yaml override.yaml > final.yaml

# Mostra configurazione risolta (con secrets mascherati)
pyw-config show --env=production

Roadmap

  • 🔐 Enhanced secrets: Integrazione con AWS Secrets Manager, Azure Key Vault
  • 🌐 Remote configs: Etcd, Consul, Kubernetes ConfigMaps
  • 📝 Configuration UI: Web interface per editing configurazioni
  • 🔄 Hot reload: Reload automatico in runtime senza restart
  • 📊 Config analytics: Metriche di utilizzo e drift detection
  • 🧩 Plugin system: Custom validators e sources

Contributing

  1. Fork il repo: git clone https://github.com/pythonWoods/pyw-config.git
  2. Crea virtual-env: poetry install && poetry shell
  3. Lancia tests: pytest
  4. Lancia linter: ruff check . && mypy
  5. Apri la PR: CI esegue tutti i check

Felice configurazione nella foresta di pythonWoods! 🌲⚙️

Links utili

Documentazione dev (work-in-progress) → https://pythonwoods.dev/docs/pyw-config/latest/

Issue tracker → https://github.com/pythonWoods/pyw-config/issues

Changelog → https://github.com/pythonWoods/pyw-config/releases

© pythonWoods — MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyw_config-0.0.0.post2.tar.gz (4.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyw_config-0.0.0.post2-py3-none-any.whl (5.4 kB view details)

Uploaded Python 3

File details

Details for the file pyw_config-0.0.0.post2.tar.gz.

File metadata

  • Download URL: pyw_config-0.0.0.post2.tar.gz
  • Upload date:
  • Size: 4.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.4

File hashes

Hashes for pyw_config-0.0.0.post2.tar.gz
Algorithm Hash digest
SHA256 52b6851709968b02e20ce7827b988cbd366de24820c59ccbddda09694adb5180
MD5 3a2048a0e58ba8180d6e04d5bbb943cd
BLAKE2b-256 14e9a877b43b7333ec8de61993ca6543e29709ca30b682c4cd19ac1c2f1029a5

See more details on using hashes here.

File details

Details for the file pyw_config-0.0.0.post2-py3-none-any.whl.

File metadata

File hashes

Hashes for pyw_config-0.0.0.post2-py3-none-any.whl
Algorithm Hash digest
SHA256 05c5d6f877d53b2692d61fd07810c95e8383a46eedf8d8e1404a3a9a8279824d
MD5 bb72738efbd0a64e0db0372a2760996c
BLAKE2b-256 e1780e9e1c9ad0479567bc5c6db88c1aa23a427197eb27bba5a96f43a78d7cb6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page