Reload bug fix
This commit is contained in:
1 parent
5dbfd2a805
commit
1626fe83bf
1 file changed
+114
@@ -0,0 +1,114 @@
|
||||
# Bug AppDaemon 4.5.10 : KeyError au rechargement des apps
|
||||
|
||||
## Symptôme
|
||||
Lors d'une modification de configuration YAML, le rechargement des apps
|
||||
échoue dans la boucle utilitaire avec cette erreur :
|
||||
|
||||
```text
|
||||
check_app_config_files → utils.deep_compare(current_app, prev_app)
|
||||
if isinstance(v, dict) and isinstance(data[k], dict):
|
||||
KeyError: 'input_selection'
|
||||
```
|
||||
|
||||
Le nom de la clé dépend de la configuration modifiée.
|
||||
|
||||
## Cause
|
||||
`deep_compare()` accède à `data[k]` sans vérifier que la clé existe dans
|
||||
l'ancienne configuration. L'ajout d'une clé contenant un dictionnaire peut
|
||||
ainsi provoquer un `KeyError`. La fonction peut aussi ignorer les clés
|
||||
supprimées, car elle parcourt uniquement la nouvelle configuration.
|
||||
|
||||
Le même problème a été signalé avec la clé `mutes` dans les commentaires de
|
||||
[AppDaemon/appdaemon#2396](https://github.com/AppDaemon/appdaemon/issues/2396).
|
||||
Le correctif du problème initial de ce ticket concerne les plugins personnalisés,
|
||||
pas nécessairement ce problème de comparaison signalé en commentaire.
|
||||
|
||||
## Statut
|
||||
- Patch local appliqué dans le conteneur sur AppDaemon **4.5.10**.
|
||||
- Le code défectueux et son appel lors du reload ont également été vérifiés
|
||||
dans la version **4.5.13** : cette mise à jour seule ne corrige pas ce bug.
|
||||
- Source de référence :
|
||||
[utils.py en 4.5.13](https://github.com/AppDaemon/appdaemon/blob/4.5.13/appdaemon/utils.py).
|
||||
- La correction compare les ensembles de clés avant de comparer les valeurs,
|
||||
y compris dans les dictionnaires imbriqués.
|
||||
|
||||
## Comment réappliquer le patch
|
||||
|
||||
Fichier concerné : `/usr/local/lib/python3.12/site-packages/appdaemon/utils.py`
|
||||
|
||||
Dans `deep_compare()`, juste après `data = data or {}`, ajouter :
|
||||
|
||||
```python
|
||||
if check.keys() != data.keys():
|
||||
return False
|
||||
```
|
||||
|
||||
Le début de la fonction devient :
|
||||
|
||||
```python
|
||||
def deep_compare(check: dict, data: dict) -> bool:
|
||||
"""Compares 2 nested dictionaries of values"""
|
||||
data = data or {} # Replaces a None value with an empty dict
|
||||
|
||||
if check.keys() != data.keys():
|
||||
return False
|
||||
|
||||
for k, v in tuple(check.items()):
|
||||
if isinstance(v, dict) and isinstance(data[k], dict):
|
||||
```
|
||||
|
||||
La suite de la fonction reste inchangée. Avant de réappliquer sur une autre
|
||||
version, vérifier son implémentation : ne pas remplacer aveuglément une
|
||||
fonction qui aurait été corrigée en amont.
|
||||
|
||||
**Redémarrer AppDaemon après application.** Modifier le fichier sur disque
|
||||
ne remplace pas la fonction déjà importée par le processus en cours ; un
|
||||
simple reload d'app ne suffit pas.
|
||||
|
||||
Ce patch réside dans `site-packages`, hors de `/conf`. Il est perdu lors
|
||||
d'une recréation du conteneur ou peut être écrasé par une réinstallation
|
||||
ou mise à jour d'AppDaemon.
|
||||
|
||||
## Comment vérifier le patch
|
||||
|
||||
Exécuter dans le conteneur :
|
||||
|
||||
```sh
|
||||
python - <<'PY'
|
||||
from appdaemon.utils import deep_compare
|
||||
|
||||
assert deep_compare({'input_selection': {}}, {}) is False
|
||||
assert deep_compare({}, {'input_selection': {}}) is False
|
||||
assert deep_compare({'x': {'a': 1}}, {'x': {}}) is False
|
||||
assert deep_compare({'x': {}}, {'x': {'a': 1}}) is False
|
||||
assert deep_compare({'x': None}, {}) is False
|
||||
assert deep_compare({'x': [1, 2]}, {'x': [1, 2]}) is True
|
||||
assert deep_compare({'x': [1, 2]}, {'x': [2, 1]}) is False
|
||||
assert deep_compare({'x': {'a': 1}}, {'x': {'a': 1}}) is True
|
||||
assert deep_compare({'x': {}}, {'x': None}) is False
|
||||
assert deep_compare({}, None) is True
|
||||
assert deep_compare({'input_selection': {}}, None) is False
|
||||
print('OK : 11 vérifications')
|
||||
PY
|
||||
```
|
||||
|
||||
Ces 11 vérifications ont réussi après application sur le module installé.
|
||||
Elles vérifient la fonction dans un nouveau processus Python, pas le reload
|
||||
complet dans le service en cours. Le service n'a pas été redémarré lors de
|
||||
l'application de ce patch.
|
||||
|
||||
## Retour arrière
|
||||
|
||||
Une sauvegarde du fichier original a été créée dans le même répertoire :
|
||||
`utils.py.before-deep-compare-fix.bak`.
|
||||
|
||||
Pour annuler le patch sur cette installation, avant toute autre modification
|
||||
ou mise à jour de `utils.py` :
|
||||
|
||||
```sh
|
||||
cp /usr/local/lib/python3.12/site-packages/appdaemon/utils.py.before-deep-compare-fix.bak \
|
||||
/usr/local/lib/python3.12/site-packages/appdaemon/utils.py
|
||||
```
|
||||
|
||||
Redémarrer ensuite AppDaemon. Ne pas restaurer cette sauvegarde sur une
|
||||
version différente d'AppDaemon.
|
||||
Reference in new issue
Block a user