zpp_config 2.2.7
Module pour le chargement et la modification de fichier de configuration
License: MIT License
Keywords: config file terminal zephyroff

# PrÃ©sentation

Ce module fournit un systÃ¨me complet de **gestion de fichiers de configuration** permettant :

- YAML / JSON / TOML  
- Templates Jinja2 (avec variables, `env()`, `vault()`, `vault_encryption()`)  
- Chargement lazy - Auto-save - Export  
- Validation via schÃ©ma hiÃ©rarchique et rÃ¨gles globales  
- AccÃ¨s dynamique par attributs _ou_ indexÃ©s (`cfg.db.host`)  
- Merge / Update sans Ã©crasement  
- Auto-reload du fichier via **watchdog**  
- CompatibilitÃ© Vault (clÃ© protÃ©gÃ©e et dÃ©cryptage)

# Installation

```console
[user@host ~]# pip install zpp_config
```

# Utilisation

### Initialisation

```python
from zpp_config import Config

config = Config("config.yaml")
```

Le backend est automatiquement choisi selon lâ€™extension du fichier, mais il est possible de dÃ©finir le type de configuration avec l'attribut **filetype** (yaml, json ou toml).

Lors de l'initialisation, il est Ã©galement possible de dÃ©finir plusieurs paramÃ¨tres qui seront utile pour le render Jinja.

**context**:  dÃ©fini un dictionnaire contenant les variables Jinja

**vault_file**: emplacement du fichier vault

**vault_keyfile**: emplacement du fichier contenant le mot de passe du vault

**vault_password**: mot de passe du vault

**vault_encryption_keyfile**: emplacement du fichier contenant le mot de passe du vault_encryption

**vault_encryption_password**: mot de passe du vault_encryption

**disable_jinja_render**: DÃ©sactive le render Jinja

```python
from core.config import Config

config = Config(
    "config.yaml",
    context={"env": "prod"},
    vault_file="secrets.vlt",
    vault_encryption_password="1234"
)
```

#### set_context()

Il est possible de redÃ©finir le context aprÃ¨s chargement grÃ¢ce Ã :

```python
config.set_context({"env": "staging", "debug": True})
config.reload()   # re-rend le template avec le nouveau contexte
```

### Chargement du fichier

Il est possible de forcer le chargement du fichier avec la mÃ©thode **load**

```python
config.load()
```

Toutefois, la configuration sera chargÃ© automatiquement lors du premier appel de la configuration.

#### Auto-reload

Il est possible d'activer l'auto-reload pour permet de recharger la configuration dans le cas oÃ¹ le fichier de configuration change

```python
config.autoreload(True)
```

Il est possible de dÃ©finir un callback a dÃ©clenchÃ© lorsque la configuration est rechargÃ©

```python
def on_reload(cfg):
    print("Configuration rechargÃ©e !")

config._on_reload = on_reload
config.autoreload(True)
```

On est Ã©galement dÃ©sactiver l'auto-reload avec:
```python
config.stop_autoreload()
```

### AccÃ¨s aux valeurs

#### Par attributs
```python
db_host = config.db.host
```
#### Par index
```python
db_host = config["db.host"]
```
#### Par get()
```python
db_host = config.get("db.host", default="127.0.0.1")
```
Par dÃ©faut, la mÃ©thode get va renvoyer un ConfigNode, mais il est possible de forcer l'envoi d'un dict brut avec dict_strict=True

#### ItÃ©ration
```python
for key, sub in config.db.items():
   print(key, sub.to_dict())
```

#### VÃ©rifier lâ€™existence dâ€™un chemin
```python
"db.host" in config   # True / False
```

### Modification des valeurs

```python
config.db.user = "admin"
config["db.password"] = "secret"
config.set("feature.enable", True)
config.delete("db.password")
```

Toutes les modifications sont gardÃ©es en mÃ©moire tant quâ€™on ne sauvegarde pas.

### Suppression des valeurs

```python
config.delete("db.password")
del config["db.password"]
del config.db.password
```

### Sauvegarde

#### Sauvegarde manuelle

Pour sauvegarder la configuration, on utilise la mÃ©thode **save**

```python
config.save()
```

L'argument **filepath** permettra de dÃ©finir le fichier de sortie.


#### Sauvegarde automatique

Il est possible d'activer la sauvegarde automatique avec **auto_save**

```python
config.auto_save(True)
```


### Merge / Update

Fusionne un dictionnaire **dans le node courant** :
```python
config.merge({"db": {"port": 3307}}, overload=True)
```


Met Ã  jour **une partie** :
```python
config.update("db", {"user": "root"})
```

- `overload=False` â†’ ne remplace pas les valeurs existantes
- `overload=True` â†’ Ã©crase celles existantes

### Vault et donnÃ©es chiffrÃ©es

Le render Jinja fournis 2 mÃ©thodes pour rÃ©cupÃ©rer des donnÃ©es chiffrÃ©es depuis un fichier vault ou un string vault_encryption

La mÃ©thode **vault** permet de rÃ©cupÃ©rer des clÃ©s depuis un vault

```python
password: "{{ vault('db_password') }}"
```

Lors de l'initialisation de la config, il faudra forcÃ©ment **vault_file** et **vault_password** ou **vault_keyfile**

La mÃ©thode **vault_encryption** permet de dÃ©chiffrer des clÃ©s vault_encryption

```python
api_key: "{{ vault_encryption('encrypted_api_key') }}"
```

Lors de l'initialisation de la config, il faudra forcÃ©ment **vault_encryption_keyfile** ou **vault_encryption_password**

### MÃ©thode complÃ©mentaire Jinja

#### MÃ©thode env

La mÃ©thode **env** permet de rÃ©cupÃ©rer des variables d'environnements

```python
debug: "{{ env('DEBUG', False) }}"
url: "https://{{ env('DOMAIN') }}/api"
```

## Enregistrement Jinja (filtres / tests / globals)

Tu peux enrichir l'environnement Jinja utilisÃ© pour rendre le fichier de config.
#### register_filter(name: str, func)

Enregistre un **filtre** Jinja utilisable dans le template.

Exemple :

```python
def join_commas(items):
     return ",".join(items)
     
config.register_filter("join_commas", join_commas)
```

Utilisation dans `config.yaml` :

```jinja2
list_as_str: "{{ mylist | join_commas }}"
```

#### register_test(name: str, func)

Enregistre un **test** Jinja (ex. `is_even` pour `if x is is_even`).

Exemple :

```python
def is_even(n):
     return isinstance(n, int) and n % 2 == 0

config.register_test("is_even", is_even)
```

Utilisation :

```jinja2
{% if myvalue is is_even %}even{% endif %}
```

#### register_global(name: str, obj: Any)

Expose un objet/fonction comme **global** dans les templates Jinja (`{{ myglobal(...) }}`).

Exemple :

```python
def now():
     from datetime import datetime
     return datetime.utcnow().isoformat()

config.register_global("now", now)
```

Utilisation :

```jinja2
generated_at: "{{ now() }}"
```

### Validation de la configuration

#### Validation via schÃ©ma hiÃ©rarchique

Il est possible de vÃ©rifier une configuration Ã  partir d'un schÃ©ma. 
Le schÃ©ma va dÃ©crire l'architecture et les prÃ©requis des clÃ©s dÃ©finis.

```python
schema = {
    "db": {
        "host": {"_type": str, "_required": True},
        "port": {"_type": int, "_min": 1, "_max": 65535}
    }
}

errors = config.validate(schema=schema)
```

#### Validation via des rÃ¨gles

Il est possible de vÃ©rifier une configuration Ã  partir d'un schÃ©ma. 
Le schÃ©ma va dÃ©crire l'architecture et les prÃ©requis des clÃ©s dÃ©finis.

```python
rules = {
    r"password$": {"_min_len": 8}
    r"hostname": {"_enum": ['localhost', '127.0.0.1']}
}
errors = config.validate(rules=rules, strict=False)
```

#### DÃ©finition possible

|RÃ¨gle|Pour|Description|
|---|---|---|
|`_required`|Tout|ClÃ© obligatoire|
|`_type`|Tout|Type Python attendu|
|`_min`, `_max`|NumÃ©rique|Limites|
|`_len`, `_min_len`, `_max_len`|ChaÃ®nes|Taille|
|`_regex`|ChaÃ®nes|Validation Regex|
|`_enum`|Tout|Liste de valeurs autorisÃ©es|
|`_not`|Tout|Valeur interdite|

### Export

```python
config.export("output.yaml", type="yaml")
config.export("output.json", type="json")
config.export("output.toml", type="toml")
```

### Conversion

Ces mÃ©thodes permettent d'obtenir des formats de sortie facilement utilisables.

### to_dict()

Retourne le contenu du nÅ“ud courant (ou racine) sous forme de `dict` Python standard.

Exemple :
```python
data = config.to_dict()
# data est un dict prÃªt Ã  Ãªtre sÃ©rialisÃ©
```

### to_json(indent: int = 4)

Retourne une chaÃ®ne JSON formatÃ©e du nÅ“ud courant :
```python
json_str = config.to_json()
```

### to_yaml(sort_keys: bool = True)

Retourne YAML sÃ©rialisÃ© :
```python
yaml_str = config.to_yaml()
```

### to_toml()

Retourne TOML sÃ©rialisÃ© (si toml est disponible) :
```python
toml_str = config.to_toml()
```

Ces mÃ©thodes nâ€™Ã©crivent pas sur le disque â€” elles renvoient des chaÃ®nes. Pour Ã©crire un fichier, utilise `export(filepath, type=...)` ou `save()`.