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, a…


This content originally appeared on DEV Community and was authored by eblocha

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 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:  &id001
    street: |
            123 Tornado Alley
            Suite 16
    city:   East Centerville
    state:  KS

ship-to:  *id001

specialDelivery:  >
    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\nSuite 16",
        "city": "East Centerville",
        "state": "KS",
    },
    "ship-to": {
        "street" : "123 Tornado Alley\nSuite 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 t
from dataclasses import dataclass
from datetime import date

@dataclass
class 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"),
        )

@dataclass
class 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"],
        )

@dataclass
class 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"],
        )

@dataclass
class 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 unittest
from .config import Order

class 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!


This content originally appeared on DEV Community and was authored by eblocha


Print Share Comment Cite Upload Translate Updates
APA

eblocha | Sciencx (2022-04-02T15:17:49+00:00) Using Dataclasses for Configuration in Python. Retrieved from https://www.scien.cx/2022/04/02/using-dataclasses-for-configuration-in-python/

MLA
" » Using Dataclasses for Configuration in Python." eblocha | Sciencx - Saturday April 2, 2022, https://www.scien.cx/2022/04/02/using-dataclasses-for-configuration-in-python/
HARVARD
eblocha | Sciencx Saturday April 2, 2022 » Using Dataclasses for Configuration in Python., viewed ,<https://www.scien.cx/2022/04/02/using-dataclasses-for-configuration-in-python/>
VANCOUVER
eblocha | Sciencx - » Using Dataclasses for Configuration in Python. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/04/02/using-dataclasses-for-configuration-in-python/
CHICAGO
" » Using Dataclasses for Configuration in Python." eblocha | Sciencx - Accessed . https://www.scien.cx/2022/04/02/using-dataclasses-for-configuration-in-python/
IEEE
" » Using Dataclasses for Configuration in Python." eblocha | Sciencx [Online]. Available: https://www.scien.cx/2022/04/02/using-dataclasses-for-configuration-in-python/. [Accessed: ]
rf:citation
» Using Dataclasses for Configuration in Python | eblocha | Sciencx | https://www.scien.cx/2022/04/02/using-dataclasses-for-configuration-in-python/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.