Source code for webspirit.config.settings

"""
Les constantes générées de manière dynamique grâce aux valeurs définies dans les fichier json de configurations.
"""


from webspirit.config.constants import DIR_DATA_SETTINGS, PATH_SETTINGS, PATH_USER

from webspirit.classes.tools.manager import BaseManager, JsonManager

from webspirit.classes.tools.contexterror import ecm, re

from webspirit.classes.tools.checktype import CheckType

from webspirit.classes.tools.typing import StrPath

from IPython.display import DisplayHandle, display

from webspirit.config.logger import DEBUG

from collections.abc import Iterable

import ipywidgets as widgets

from types import ModuleType

from typing import Any

import json, os



[docs] class Parameter(dict): def __init__(self, name: str, schema: StrPath = PATH_SETTINGS, user: StrPath = PATH_USER, dir_config: StrPath = DIR_DATA_SETTINGS): self.dir_config = dir_config self.schema = JsonManager(schema) self.user = JsonManager(user) self.name = name if self.name in self.schema: self.meta = self.schema[self.name] else: re(f"Le paramètre '{self.name}' n'existe pas dans la configuration {self.schema.path.relpath()}", KeyError) super().__init__(self.meta) def __str__(self) -> str: return self.name def __repr__(self) -> str: return f"{self.name}: {self.resource}"
[docs] def get_default(self) -> Any: """Récupère la valeur par défaut du paramètre en fonction des différentes sources possibles""" if self.name in self.user: return self.user[self.name] elif self.default is not None: return self.default elif self.object is not None: return self.get_default_obj() re(f"Le paramètre '{self.name}' n'a pas de valeur défini correctement dans '{self.schema.path.relpath()}'")
[docs] def get_default_obj(self) -> Any: """Récupère la valeur par défaut du paramètre en fonction de l'objet source et des options pré-définies""" if isinstance(self.obj, Iterable): if self.value in self.obj: return self.value else: re(f"Le paramètre '{self.name}' défini par défaut dans le champs source avec '{self.value}' n'existe pas dans {self.obj}", ValueError) elif hasattr(self.obj, '__getitem__') and self.index is not None: return self.obj[self.index] elif self.attr is not None and hasattr(self.obj, self.attr): return getattr(self.obj, self.attr) return self.obj
[docs] def get_options(self) -> Iterable | None: """Récupère les valeurs possibles du paramètre si l'objet source est itérable""" return list(self.obj) if isinstance(self.obj, Iterable) else None
[docs] def get_obj(self) -> Any | None: """Récupère l'objet source défini dans le paramètre de configuration""" if self.object is None: return None lib: list[str] = self.object.split('.') with ecm(_raise=True): module: ModuleType = __import__('.'.join(lib[:-1]), fromlist=[lib[-2]]) if (obj := getattr(module, lib[-1], None)) is None: re(f"Le module '{module}' n'a pas d'attribut '{lib[-1]}' pour le paramètre {self.name}", AttributeError) os.chdir(self.dir_config) if hasattr(obj, '__call__') and len(params := self.params): obj = obj(*params) return obj
[docs] def serial(self) -> Any | None: """Renvoi le paramètre serializable avec les normes d'un fichier json""" with ecm(f"Impossible de sérialiser la valeur '{self.resource}' de type '{type(self.resource)}' du paramètre '{self.name}'", level=DEBUG): json.dumps(self.resource) return self.resource return str(self.resource)
[docs] def get_widgets(self) -> Any | None: """Génère un widget ipywidgets en fonction du type de paramètre défini""" if self.type == 'bool': return widgets.Checkbox(value=self.resource, description=self.label) elif self.type == 'text': return widgets.Text(value=self.resource or '', description=self.label) elif self.type == 'list': return widgets.Dropdown(options=self.options, value=self.resource, description=self.label) elif self.type == 'path': return widgets.Text(value=self.resource or '', description=self.label, placeholder="Chemin du dossier/fichier ...") return None
@property def resource(self) -> Any: """La valeur du paramètre dans la configuration""" return self.get_default() @property def options(self) -> Iterable | None: """Si défini, les valeurs possibles du paramètre""" return self.get_options() @property def obj(self) -> Any | None: """Si défini, l'objet source qui permet de récupérer la valeur du paramètre""" return self.get_obj() @property def type(self) -> str | None: return self.meta.get('type', None) @property def label(self) -> str | None: return self.meta.get('label', None) @property def default(self) -> Any | None: return self.meta.get('default', None) @property def source(self) -> dict | None: return self.meta.get('source', None) @property def object(self) -> str | None: return self.source.get('object', None) @property def params(self) -> list[Any]: return self.source.get('params', []) @property def default_src(self) -> dict: return self.source.get('default', {}) @property def value(self) -> Any | None: return self.default_src.get('value', None) @property def attr(self) -> Any | None: return self.default_src.get('attr', None) @property def index(self) -> Any | None: return self.default_src.get('index', None)
[docs] class ConfigManager(dict): @CheckType def __init__(self, schema: StrPath = PATH_SETTINGS, user: StrPath = PATH_USER, dir_config: StrPath = DIR_DATA_SETTINGS): self.dir_config = dir_config self.schema = JsonManager(schema) self.user = JsonManager(user) self.config: dict[str, Parameter] = self.build() self.user.save({ name: parameter.serial() for name, parameter in self.config.items() }) super().__init__(self.user.to_dict()) def __str__(self) -> str: return json.dumps({ name: parameter.serial() for name, parameter in self.config.items() }, indent=2, ensure_ascii=False) __repr__ = __str__
[docs] def build(self) -> dict[str, Parameter]: """Construit les paramètres de configuration avec leurs valeurs respectives à partir du schéma défini""" return { name: Parameter(name, self.schema.path, self.user.path, self.dir_config) for name in self.schema }
[docs] def save(self): """Sauvegarde la configuration actuelle dans le fichier utilisateur""" self.user.save(self)
[docs] def reset(self, *parameters: list[str] | None): """Supprime le fichier avec les données de l'utilisateur si parameters est null, sinon supprime les paramètres donnés dans parameters Args: parameters (list[str] | None, optional): La liste de paramètres à supprimer. Defaults to None. """ if not len(parameters): self.clear() self.save() self.config = self.build() else: for name in parameters: if name in self: self.pop(name) self.save()
SETTINGS: ConfigManager = ConfigManager(PATH_SETTINGS, PATH_USER, DIR_DATA_SETTINGS)
[docs] class IPythonConfig: def __init__(self, manager: ConfigManager = SETTINGS): self.manager = manager self.widgets_map: dict[str, Any | None] = {} self.ui_elements: list[Any | None] = [] def _repr_pretty_(self, *_) -> DisplayHandle: return self.display()
[docs] def save_config(self, *_): self.manager.user.save({ k: w.value for k, w in self.widgets_map.items() }) self.status.value = "<b style='color:green'>Configuration sauvegardée ✔</b>" self.json.value = f"<i>{self.manager}</i>"
[docs] def reset_config(self, *_): self.manager.reset() self.status.value = "<b style='color:orange'>Configuration réinitialisée</b>" self.json.value = f"<i>{self.manager}</i>"
[docs] def display(self) -> DisplayHandle: """Construit et affiche l'interface de configuration interactive dans un notebook IPython""" self.status = widgets.HTML() self.status.value = "<b style='color:blue'>Initialisation de la configuration</b>" self.json = widgets.HTML() self.json.value = f"<i>{self.manager}</i>" for parameter in self.manager.config.values(): widget = parameter.get_widgets() self.widgets_map[parameter.name] = widget self.ui_elements.append(widget) save = widgets.Button(description='💾 Save', button_style='success') save.on_click(self.save_config) reset = widgets.Button(description='🧽 Reset', button_style='warning') reset.on_click(self.reset_config) return display( widgets.VBox([ widgets.HTML('<h2>⚙️ Configuration WebSpirit</h2>'), widgets.VBox(self.ui_elements, layout=widgets.Layout(gap="20px")), widgets.HBox([save, reset]), self.status, self.json, ]) )
INTERACTIVE: IPythonConfig = IPythonConfig(SETTINGS) __all__: list[str] = [ 'Parameter', 'ConfigManager', 'IPythonConfig', 'SETTINGS', 'INTERACTIVE', ]