Code source de pyspc.core.config

#!/usr/bin/python3
# -*- coding: utf-8 -*-
########################################################################
#
# This file is part of python module <pyspc>.
# Copyright (C) 2013-2020  R. Marty
#   (renaud.marty@developpement-durable.gouv.fr)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see COPYING.txt).
# If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
"""
Objets natifs et convention de pyspc - Fichiers de configuration
"""
import configparser
import collections

from pyspc.core._config import CONVERT, IO


[docs] class Config(collections.OrderedDict, CONVERT, IO): """ Fichiers de configuration Attributes ---------- filename : str Nom du fichier de configuration """
[docs] def __init__(self, filename=None): """ Initialisation de l'instance Config Parameters ---------- filename : str Nom du fichier de configuration """ super().__init__() self.filename = filename
def __str__(self): """ Afficher les méta-données de l'instance <Config> """ cname = self.__class__.__name__ text = """ ************************************* ************ {cname} ***************** ************************************* * NOM FICHIER = {filename} * CONFIGURATION {content} ************************************* """ content = "" for k, v in self.items(): if isinstance(v, str): content += f'\n * + {k} = {v}' elif isinstance(v, (dict, collections.OrderedDict)): content += f'\n * + {k}' for k2, v2 in v.items(): content += f'\n * + {k2} = {v2}' return text.format(cname=cname, filename=self.filename, content=content)
[docs] def convert(self, functions=None): """ Convertir les valeurs de la configuration Parameters ---------- functions : dict Fonctions de conversion à appliquer - clé : (section, option) - valeur : fonction de conversion """ # --------------------------------------------------------------------- # 0- Contrôles # --------------------------------------------------------------------- if not self: raise ValueError('La configuration est vide') if not isinstance(functions, dict): raise ValueError('Fonctions de conversion mal-définies') # --------------------------------------------------------------------- # 1- Conversion # --------------------------------------------------------------------- for key in functions: section = key[0] option = key[1] if section in self and option in self[section]: try: self[section][option] = \ functions[key](self[section][option]) except ValueError: pass except TypeError: pass
[docs] def list_ordered_options(self): """ Lister les options UNIQUES de la configuration, avec tri """ ops = [] for s in self: ops.extend([o for o in self[s] if o not in ops]) return ops
[docs] def list_unique_options(self): """ Lister les options UNIQUES de la configuration, avec tri """ return sorted(list({o for s in self for o in self[s]}))
[docs] def list_sections_options(self): """ Lister les sections et options de la configuration """ return [(s, o) for s in self for o in self[s]]
[docs] def read(self, encoding='utf-8'): """ Lire un fichier de configuration Parameters ---------- encoding : str Encodage du fichier de configuration 'utf-8' par défaut """ cfg_parser = configparser.ConfigParser() cfg_parser.optionxform = str self.clear() with open(self.filename, 'r', encoding=encoding) as f: cfg_parser.read_file(f, self.filename) for section in cfg_parser.sections(): self.setdefault(section, collections.OrderedDict()) for option in cfg_parser.options(section): self[section].setdefault(option, cfg_parser.get(section, option))
[docs] def update_config(self, config=None, overwrite=None, strict=None): """ Mettre à jour la configuration à partir d'un dictionnaire Parameters ---------- config : dict, Config Eléments à mettre à jour. {(section, option) : valeur} overwrite : bool Forcer l'écriture si existant, par défaut: True strict : bool Ne considérer que les clés existantes, par défaut: True """ # --------------------------------------------------------------------- # 0- Contrôles # --------------------------------------------------------------------- if isinstance(config, Config): config = {(s, o): config[s][o] for s in config for o in config[s]} if not isinstance(config, dict): raise ValueError('Fichiers de configuration mal-definis') if overwrite is None: overwrite = True if not isinstance(overwrite, bool): raise ValueError("L'argument 'overwrite' doit etre un booleen") if strict is None: strict = True if not isinstance(strict, bool): raise ValueError("L'argument 'strict' doit etre un booleen") # --------------------------------------------------------------------- # 1- Clés de configuration # --------------------------------------------------------------------- so = set(self.list_sections_options()) new = set(list(config.keys())) # if strict: # for n in new.difference(so): # print("La cle {} n'existe pas dans la configuration actuelle" # ". Veuillez mettre strict=False pour forcer la " # "mise-a-jour".format(n)) # --------------------------------------------------------------------- # 2- Mise à jour 'strict == True' # --------------------------------------------------------------------- for n in new.intersection(so): s = n[0] o = n[1] if not overwrite: self[s].setdefault(o, config[n]) else: self[s][o] = config[n] # --------------------------------------------------------------------- # 3- Mise à jour complémentaire si 'strict == False' # --------------------------------------------------------------------- if not strict: for n in sorted(new.difference(so)): s = n[0] o = n[1] self.setdefault(s, collections.OrderedDict()) self[s][o] = config[n]
[docs] def write(self, encoding='utf-8', newline='\n', func_sec=None, func_opt=None): """ Écrire un fichier de configuration. Parameters ---------- encoding : str Encodage du fichier de configuration, 'utf-8' par défaut newline : str Charactère de nouvelle ligne, '\\n' par défaut func_sec : function, dict Fonction appliquée pour convertir les sections en str. Peut-être défini par un dictionnaire {section: function} func_opt : function, dict Fonction appliquée pour convertir les options en str Peut-être défini par un dictionnaire {(section, option): function} """ cfg_parser = configparser.ConfigParser() cfg_parser.optionxform = str if func_sec is None: func_sec = str func_sec = {section: func_sec.get(section, str) if isinstance(func_sec, dict) else func_sec for section in self.keys()} if func_opt is None: func_opt = str func_opt = {(section, option): func_opt.get((section, option), str) if isinstance(func_opt, dict) else func_opt for section in self.keys() for option in self[section]} for section in self.keys(): sas = func_sec[section](section) cfg_parser.add_section(sas) for option, value in self[section].items(): oas = func_opt[(section, option)](option) value = str(value) cfg_parser.set(sas, oas, value) with open(self.filename, 'w', encoding=encoding, newline=newline) as f: cfg_parser.write(f)