Heim > Backend-Entwicklung > Python-Tutorial > Auf dem Weg zu mühelosen Python-Konfigurationsdateien Version 3

Auf dem Weg zu mühelosen Python-Konfigurationsdateien Version 3

Barbara Streisand
Freigeben: 2024-12-20 14:26:16
Original
349 Leute haben es durchsucht

Einführung

Dies ist der letzte Artikel dieser Serie. Mit dieser Implementierung soll der Hauptnachteil des Boiler-Plate-Codes behoben werden, den ich im vorherigen Artikel beschrieben habe. Ich nenne diese Implementierung eine dynamische Eigenschaftsklasse.

Klassenvertretung

Das folgende Klassendiagramm zeigt die wiederverwendbare Klasse DynamicConfiguration und die unterstützenden Datenstrukturen, die ein Entwickler zur Nutzung dieser Funktionalität benötigt. Es bietet weiterhin alle Grundfunktionen von Version 2, einschließlich Auto-Boot-Strapping, Erstellung fehlender Abschnitte und Schlüsselwerte.

Towards Effortless Python Configuration Files Version 3

Beschreibung des Entwicklercodes

Ich werde den vollständigen Quellcode für eine Anwendung präsentieren, die diese Klasse verwenden möchte. Ich verwende die vorherigen Eigenschaften, die wir in den vorherigen drei Artikeln besprochen haben.

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)

Nach dem Login kopieren

Zeilen 45–50 sind der Code, den Sie schreiben müssen. Im Wesentlichen stellen Sie lediglich sicher, dass Sie den Dateinamen, den Modulnamen und die Konfigurationsabschnitte übergeben. Dieser Abschnittstyp stammt aus dem DynamicConfiguration-Modul.

Zeilen 21–28 und 30–36 sind ValueDescriptions-Wörterbücher. Der KeyName ist die Eigenschaft und verweist auf eine ValueDescription. Beachten Sie, dass der Indikator zum Beibehalten einer Aufzählung vom Dekorator der vorherigen Implementierung in ein boolesches Attribut in einer ValueDescription verschoben wurde.

Beschreibung des Implementierungscodes

Wenn Sie sich das Klassendiagramm für DynamicConfiguration genau ansehen, werden Sie feststellen, dass es zwei Python-magic-Methoden implementiert. Es handelt sich um die Methoden __getattr__(self, name)__ und __setattr__(self, name, value)__.

  • __getattr__(self, name)__ Ermöglicht einem Entwickler, das Verhalten zu definieren, wenn ein Klassenkonsument versucht, auf ein nicht vorhandenes Attribut zuzugreifen.
  • __setattr__(self, name, value)__ Ermöglicht einem Entwickler, Verhalten für die Zuweisung zu einem Attribut zu definieren.

Das Folgende ist der Code für __getattr__. Dies ähnelt stark dem Dekorator, den wir in Version 2 verwendet haben. Die Schlüsselarbeit erfolgt beim Aufruf der geschützten Methode _lookupKey() in Zeile 14. Es gibt eine vollständige Beschreibung des Attributs zurück, damit wir den Attributabruf simulieren können.

    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

Nach dem Login kopieren

Das Folgende ist die Implementierung von__setattr__(). Beachten Sie die Unterstützung für Aufzählungen in den Zeilen 22–27 und die Funktion Durchschreiben in Zeile 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()
Nach dem Login kopieren

Auf Eigenschaften zugreifen und diese ändern

Der Zugriff auf und die Änderung von Eigenschaften erfolgt genau wie in 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=}')
Nach dem Login kopieren

Das obige Snippet erzeugt die folgende Ausgabe.

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'
Nach dem Login kopieren

Abschluss

Der Quellcode für diesen Artikel ist hier. Siehe die Support-Klasse SingletonV3. Sehen Sie sich die Implementierung von

an

Vorteile

  • Einfacher typsicherer Zugriff auf Anwendungseigenschaften
  • Wiederverwendbare übergeordnete Klasse für verschiedene Implementierungen
  • Datenstrukturgesteuerter Code zum Hinzufügen neuer Abschnitte und Konfigurationsschlüssel
  • Kein Boiler-Plate-Code für Eigenschaften

Nachteile

  • Da keine tatsächlichen Eigenschaften implementiert sind, erhalten wir dafür keine IDE-Unterstützung
  • Darüber hinaus können aufgrund der Suchmethoden für Schlüssel verschiedene Schlüssel in verschiedenen Abschnitten nicht denselben Namen haben

Das obige ist der detaillierte Inhalt vonAuf dem Weg zu mühelosen Python-Konfigurationsdateien Version 3. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage