Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 2, 2022 03:17 pm GMT

Using Dataclasses for Configuration in Python

Introduction

Defining configuration schemes for python apps can be tricky. In this article, I will showcase a simple and effective way to make handling configuration easy, with only the standard library.

Format

For this method, any file format will work, as long as you can parse it into a python dict.

A Simple Example

We'll make a simple example with this made-up config from wikipedia:

---receipt:     Oz-Ware Purchase Invoicedate:        2012-08-06customer:    first_name:   Dorothy    family_name:  Galeitems:    - part_no:   A4786      descrip:   Water Bucket (Filled)      price:     1.47      quantity:  4    - part_no:   E1628      descrip:   High Heeled "Ruby" Slippers      size:      8      price:     133.7      quantity:  1bill-to:  &id001    street: |            123 Tornado Alley            Suite 16    city:   East Centerville    state:  KSship-to:  *id001specialDelivery:  >    Follow the Yellow Brick    Road to the Emerald City.    Pay no attention to the    man behind the curtain.

We can parse this into python to get a dict:

config = {    "reciept": "Oz-Ware Purchase Invoice",    "date": "2012-08-06",    "customer": {        "first_name": "Dorothy",        "family_name": "Gale",    },    "items": [        {            "part_no": "A4786",            "descrip": "Water Bucket (Filled)",            "price": 1.47,            "quantity": 4,        },        {            "part_no": "E1628",            "descrip": "High Heeled \"Ruby\" Slippers",            "size": 8,            "price": 133.7,            "quantity": 1,        },    ],    "bill-to": {        "street" : "123 Tornado Alley
Suite 16", "city": "East Centerville", "state": "KS", }, "ship-to": { "street" : "123 Tornado Alley
Suite 16", "city": "East Centerville", "state": "KS", }, "specialDelivery": "Follow the Yellow Brick Road to the Emerald City. Pay no attention to the man behind the curtain.",}

However, using this in the code is cumbersome. config["customer"]["first_name"] is prone to error, and difficult to refactor.

Dataclasses

Dataclasses will make our life much easier. We define the config properties, sub-properties, and types in a config.py file:

import typing as tfrom dataclasses import dataclassfrom datetime import date@dataclassclass Item:    part_no: str    description: str    price: float    quantity: int    size: int = None    def __post_init__(self):        # Do some validation        if self.quantity <= 0:            raise ValueError("quantity must be greater than zero")    @classmethod    def from_dict(cls: t.Type["Item"], obj: dict):        return cls(            part_no=obj["part_no"],            description=obj["descrip"],            price=obj["price"],            quantity=obj["quantity"],            size=obj.get("size"),        )@dataclassclass Customer:    first_name: str    family_name: str    @classmethod    def from_dict(cls: t.Type["Customer"], obj: dict):        return cls(            first_name=obj["first_name"],            last_name=obj["family_name"],        )@dataclassclass Address:    street: str    city: str    state: str    @classmethod    def from_dict(cls: t.Type["Address"], obj: dict):        return cls(            street=obj["street"],            city=obj["city"],            state=obj["state"],        )@dataclassclass Order:    reciept: str    date: date    customer: Customer    items: t.Sequence[Item]    bill_to: Address    ship_to: Address    special_delivery: str = None    @classmethod    def from_dict(cls: t.Type["Order"], obj: dict):        return cls(            receipt=obj["reciept"],            date=date(obj["date"]),            customer=Customer.from_dict(obj["customer"]),            items=[Item.from_dict(item) for item in obj["items"]),            bill_to=Address.from_dict(obj["bill-to"]),            ship_to=Address.from_dict(obj["ship-to"]),            special_delivery=obj.get("specialDelivery"),        )

Now, when we want to use the config in our application, we can simply do:

raw_config = {...}config = Order.from_dict(raw_config)config.customer.first_name

This method has a ton of benefits:

  • We get code completion and type hints in the editor
  • It's easier to maintain, since you only have to change a config property name in one place
  • Can implement version reconciliation in the from_dict method
  • Refactoring is a breeze, since editors can auto-refactor class property names
  • Allows you to define configurations with python code, since you can instantiate the dataclasses directly in a settings.py file, for example
  • It's testable:
import unittestfrom .config import Orderclass TestOrderConfig(unittest.TestCase):    def test_example_config(self):        raw_config = {...}        expected = Order(            customer=Customer(...),            ...        )        self.assertEqual(Order.from_dict(raw_config), expected)

Hopefully you found this useful, and can use this method to clean up some of your projects!


Original Link: https://dev.to/eblocha/using-dataclasses-for-configuration-in-python-4o53

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To