An Interest In:
Web News this Week
- April 27, 2024
- April 26, 2024
- April 25, 2024
- April 24, 2024
- April 23, 2024
- April 22, 2024
- April 21, 2024
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
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To