Maison > développement back-end > Tutoriel Python > Vers une version 3 des fichiers de configuration Python sans effort

Vers une version 3 des fichiers de configuration Python sans effort

Barbara Streisand
Libérer: 2024-12-20 14:26:16
original
396 Les gens l'ont consulté

Introduction

Ceci est le dernier article de cette série. Cette implémentation vise à corriger le principal inconvénient du code de plaque passe-partout que j'ai décrit dans l'article précédent. J'appelle cette implémentation une classe de propriétés dynamiques.

Représentation de classe

Le diagramme de classes suivant montre la classe réutilisable DynamicConfiguration et les structures de données de support nécessaires pour qu'un développeur puisse utiliser cette fonctionnalité. Il fournit toujours toutes les fonctionnalités de base de la version 2, y compris le cerclage de démarrage automatique, la création de sections manquantes et de valeurs clés.

Towards Effortless Python Configuration Files Version 3

Description du code du développeur

Je vais présenter le code source complet d'une application cherchant à utiliser cette classe. J'utilise les propriétés précédentes dont nous avons discuté dans les 3 articles précédents.

from codeallybasic.DynamicConfiguration import DynamicConfiguration
from codeallybasic.DynamicConfiguration import KeyName
from codeallybasic.DynamicConfiguration import SectionName
from codeallybasic.DynamicConfiguration import Sections
from codeallybasic.DynamicConfiguration import ValueDescription
from codeallybasic.DynamicConfiguration import ValueDescriptions
from codeallybasic.SecureConversions import SecureConversions

from codeallybasic.SingletonV3 import SingletonV3

from ByteSizedPython.ImpostorEnumByName import ImpostorEnumByName
from ByteSizedPython.PhoneyEnumByValue import PhoneyEnumByValue

LOGGER_NAME:       str = 'Tutorial'
BASE_FILE_NAME: str = 'config.ini'
MODULE_NAME:       str = 'version3properties'

DEFAULT_PHONEY_ENUM_BY_VALUE:  PhoneyEnumByValue  = PhoneyEnumByValue.FakeBrenda
DEFAULT_IMPOSTOR_ENUM_BY_NAME: ImpostorEnumByName = ImpostorEnumByName.High

GENERAL_PROPERTIES: ValueDescriptions = ValueDescriptions(
    {
        KeyName('debug'):    ValueDescription(defaultValue='False', deserializer=SecureConversions.secureBoolean),
        KeyName('logLevel'): ValueDescription(defaultValue='Info'),
        KeyName('phoneyEnumByValue'):  ValueDescription(defaultValue=DEFAULT_PHONEY_ENUM_BY_VALUE.value,  enumUseValue=True),
        KeyName('impostorEnumByName'): ValueDescription(defaultValue=DEFAULT_IMPOSTOR_ENUM_BY_NAME.name,  enumUseName=True),
    }
)

DATABASE_PROPERTIES: ValueDescriptions = ValueDescriptions(
    {
        KeyName('dbName'): ValueDescription(defaultValue='dbName'),
        KeyName('dbHost'): ValueDescription(defaultValue='localhost'),
        KeyName('dbPort'): ValueDescription(defaultValue='5342', deserializer=SecureConversions.secureInteger),
    }
)

CONFIGURATION_SECTIONS: Sections = Sections(
    {
        SectionName('General'):  GENERAL_PROPERTIES,
        SectionName('Database'): DATABASE_PROPERTIES,
    }
)

class ConfigurationPropertiesVersion3(DynamicConfiguration, metaclass=SingletonV3):
    def __init__(self):

        self._logger: Logger = getLogger(LOGGER_NAME)

        super().__init__(baseFileName=BASE_FILE_NAME, moduleName=MODULE_NAME, sections=CONFIGURATION_SECTIONS)

Copier après la connexion

Les lignes 45 à 50 sont le code que vous devez écrire. Essentiellement, vous vous assurez simplement de transmettre le nom du fichier, le nom du module et les sections de configuration. Ce type Sections provient du module DynamicConfiguration.

Les lignes 21 à 28 et 30 à 36 sont des dictionnaires ValueDescriptions. Le KeyName est la propriété et pointe vers une ValueDescription. Notez que l'indicateur sur la façon de conserver une énumération est déplacé du décorateur de l'implémentation précédente vers un attribut booléen dans une ValueDescription.

Description du code de mise en œuvre

Si vous regardez attentivement le diagramme de classes de DynamicConfiguration, vous verrez qu'il implémente deux méthodes Python magic. Ce sont les méthodes __getattr__(self, name)__ et __setattr__(self, name, value)__.

  • __getattr__(self, name)__ Permet à un développeur de définir un comportement lorsqu'un consommateur de classe tente d'accéder à un attribut inexistant.
  • __setattr__(self, name, value)__ Permet à un développeur de définir un comportement pour l'affectation à un attribut.

Ce qui suit est le code pour __getattr__. Cela ressemble beaucoup au décorateur que nous avons utilisé dans la version 2. Le travail clé se produit lors de l'appel à la ligne 14 de la méthode protégée _lookupKey(). Il renvoie une description complète de l'attribut afin que nous puissions simuler la récupération de l'attribut.

    def __getattr__(self, attrName: str) -> Any:
        """
        Does the work of retrieving the named attribute from the configuration parser

        Args:
            attrName:

        Returns:  The correctly typed value
        """

        self._logger.info(f'{attrName}')

        configParser: ConfigParser     = self._configParser
        result:       LookupResult     = self._lookupKey(searchKeyName=KeyName(attrName))
        valueDescription: ValueDescription = result.keyDescription

        valueStr: str = configParser.get(result.sectionName, attrName)

        if valueDescription.deserializer is not None:
            value: Any = valueDescription.deserializer(valueStr)
        else:
            value = valueStr

        return value

Copier après la connexion

Ce qui suit est l'implémentation de __setattr__(). Notez la prise en charge des énumérations aux lignes 22 à 27 et la fonctionnalité écriture directe à la ligne 30.

    def __setattr__(self, key: str, value: Any):
        """
        Do the work of writing this back to the configuration/settings/preferences file
        Ignores protected and private variables uses by this class

        Does a "write through" to the backing configuration file (.ini)

        Args:
            key:    The property name
            value:  Its new value
        """

        if key.startswith(PROTECTED_PROPERTY_INDICATOR) or key.startswith(PRIVATE_PROPERTY_INDICATOR):
            super(DynamicConfiguration, self).__setattr__(key, value)
        else:
            self._logger.debug(f'Writing `{key}` with `{value}` to configuration file')

            configParser: ConfigParser  = self._configParser
            result:       LookupResult  = self._lookupKey(searchKeyName=KeyName(key))
            valueDescription: ValueDescription = result.keyDescription

            if valueDescription.enumUseValue is True:
                valueStr: str = value.value
                configParser.set(result.sectionName, key, valueStr)
            elif valueDescription.enumUseName is True:
                configParser.set(result.sectionName, key, value.name)
            else:
                configParser.set(result.sectionName, key, str(value))

            self.saveConfiguration()
Copier après la connexion

Accès et modification des propriétés

L'accès et la modification des propriétés sont exactement les mêmes que dans la version 2.

    basicConfig(level=INFO)

    config: ConfigurationPropertiesVersion2 = ConfigurationPropertiesVersion2()

    logger: Logger = getLogger(LOGGER_NAME)

    logger.info(f'{config.debug=}')
    logger.info(f'{config.logLevel=}')
    logger.info(f'{config.phoneyEnumByValue=}')
    logger.info(f'{config.impostorEnumByName=}')
    logger.info('Database Properties Follow')
    logger.info(f'{config.dbName=}')
    logger.info(f'{config.dbHost=}')
    logger.info(f'{config.dbPort=}')
    logger.info('Mutate Enumeration Properties')
    config.phoneyEnumByValue = PhoneyEnumByValue.TheWanderer
    logger.info(f'{config.phoneyEnumByValue=}')
    config.impostorEnumByName = ImpostorEnumByName.Low
    logger.info(f'{config.impostorEnumByName=}')
Copier après la connexion

L'extrait ci-dessus produit le résultat suivant.

INFO:Tutorial:config.debug='False'
INFO:Tutorial:config.logLevel='Info'
INFO:Tutorial:config.phoneyEnumByValue=<PhoneyEnumByValue.FakeBrenda: 'Faker Extraordinaire'>
INFO:Tutorial:config.impostorEnumByName='High'
INFO:Tutorial:Database Properties Follow
INFO:Tutorial:config.dbName='example_db'
INFO:Tutorial:config.dbHost='localhost'
INFO:Tutorial:config.dbPort=5432
INFO:Tutorial:Mutate Enumeration Properties
INFO:Tutorial:config.phoneyEnumByValue=<PhoneyEnumByValue.TheWanderer: 'The Wanderer'>
INFO:Tutorial:config.impostorEnumByName='Low'
Copier après la connexion

Conclusion

Le code source de cet article est ici. Voir la classe de support SingletonV3. Voir la mise en œuvre de

Avantages

  • Accès facile et sécurisé aux propriétés de l'application
  • Classe parent réutilisable pour différentes implémentations
  • Code basé sur la structure des données pour ajouter de nouvelles sections et clés de configuration
  • Pas de code de plaque passe-partout pour les propriétés

Inconvénients

  • Puisqu'aucune propriété réelle n'est implémentée, nous n'obtenons pas de support IDE pour elles
  • De plus, en raison des méthodes de recherche des clés, différentes clés dans différentes sections ne peuvent pas avoir le même nom

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal