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.
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.
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)
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.
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)__.
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
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()
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=}')
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'
Le code source de cet article est ici. Voir la classe de support SingletonV3. Voir la mise en œuvre de
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!