A Python library for recursively loading and updating dataclasses from nested dictionaries
Project description
CK Recursive Dataclass
A Python library that extends Python's dataclasses to support recursive serialization and deserialization of nested dataclass structures. This library makes it easy to work with complex, nested data structures while maintaining type safety and providing convenient conversion methods between dataclasses and dictionaries.
Features
- 🔄 Recursive conversion between dataclasses and dictionaries
- 🌳 Support for multiple nested dataclass structures
- 📦 Handle dictionaries and lists of dataclasses
- 📝 Type-safe with full mypy support
- ✨ Optional field support with None handling
- 🔍 Type preservation with type field
- 🚀 Easy serialization for API responses
- 🐍 Compatible with Python 3.11 and 3.12
Installation
pip install ck-recursive-dataclass
Quick Start
Here's a simple example of how to use recursive dataclasses:
from dataclasses import dataclass
from ck_recursive_dataclass import RecursiveDataclass
from typing import Optional, Dict
@dataclass
class Address(RecursiveDataclass):
street: str
city: str
country: str
postal_code: Optional[str] = None
@dataclass
class Occupation(RecursiveDataclass):
title: str
company: str
years_experience: int
department: Optional[str] = None
@dataclass
class Person(RecursiveDataclass):
name: str
age: int
addresses: Dict[str, Address]
occupation: Occupation
email: Optional[str] = None
# Create instances
home_address = Address(
street="123 Home St",
city="Hometown",
country="Homeland",
postal_code="12345"
)
work_address = Address(
street="456 Work Ave",
city="Workville",
country="Workland"
)
occupation = Occupation(
title="Senior Developer",
company="Tech Corp",
years_experience=5,
department="Engineering"
)
person = Person(
name="John Doe",
age=30,
addresses={"home": home_address, "work": work_address},
occupation=occupation,
email="john@example.com"
)
# Convert to dictionary
person_dict = person.to_dict()
print("Person as dictionary:")
print(person_dict)
# Output:
# {
# 'name': 'John Doe',
# 'age': 30,
# 'addresses': {
# 'home': {
# 'street': '123 Home St',
# 'city': 'Hometown',
# 'country': 'Homeland',
# 'postal_code': '12345',
# '__type__': 'Address'
# },
# 'work': {
# 'street': '456 Work Ave',
# 'city': 'Workville',
# 'country': 'Workland',
# 'postal_code': None,
# '__type__': 'Address'
# }
# },
# 'occupation': {
# 'title': 'Senior Developer',
# 'company': 'Tech Corp',
# 'years_experience': 5,
# 'department': 'Engineering',
# '__type__': 'Occupation'
# },
# 'email': 'john@example.com',
# '__type__': 'Person'
# }
# Create from dictionary
new_person = Person.from_dict(person_dict)
Features in Detail
Multiple Nested Dataclasses
The library handles multiple levels of nested dataclass structures automatically. For example, a Person can have both addresses and occupation as nested dataclasses:
person = Person(
name="John Doe",
addresses={"home": home_address}, # Address dataclass
occupation=occupation, # Occupation dataclass
age=30
)
Dictionary of Dataclasses
You can use dictionaries with dataclass values, and the library will handle the conversion properly:
addresses = {
"home": Address("123 Home St", "Hometown", "Homeland"),
"work": Address("456 Work Ave", "Workville", "Workland")
}
Nested Dataclass Support
The library handles nested dataclass structures automatically, maintaining type information and validation throughout the conversion process.
Optional Fields
Fields marked as Optional will be properly handled during conversion:
@dataclass
class User(RecursiveDataclass):
username: str
email: Optional[str] = None
Type Safety
The library is fully type-hinted and works well with mypy for static type checking. During conversion:
- Type information is preserved using the
__type__field - Required fields are validated
- Type mismatches are caught during deserialization
# Type validation during deserialization
try:
# This will raise ValueError due to missing required field 'street'
invalid_address = Address.from_dict({
"city": "New York",
"country": "USA"
})
except ValueError as e:
print(f"Validation error: {e}") # Missing required field street
try:
# This will raise TypeError due to invalid input type
invalid_person = Person.from_dict("not a dictionary")
except TypeError as e:
print(f"Type error: {e}") # Expected dict, got str
Serialization Support
The library is particularly useful for API development where you need to:
- Serialize complex objects to JSON-compatible dictionaries
- Deserialize nested JSON data into typed Python objects
- Maintain type safety throughout the process
For example:
# API response serialization
@app.get("/user/{user_id}")
def get_user(user_id: str):
user = get_user_from_db(user_id) # Returns Person object
return user.to_dict() # Returns JSON-serializable dictionary
# API request deserialization
@app.post("/user")
def create_user(user_data: dict):
user = Person.from_dict(user_data) # Converts dictionary to Person object
save_user_to_db(user)
return {"status": "success"}
Development
Prerequisites
- Python 3.11 or higher
- tox for running tests
Setting Up Development Environment
- Clone the repository:
git clone https://github.com/kimcharli/ck_recursive_dataclass.git
cd ck_recursive_dataclass
- Install development dependencies:
pip install tox
- Run tests:
tox
Running Tests
The project uses tox to run tests across different Python versions and environments:
- Python 3.11 and 3.12 environments for compatibility testing
- Type checking with mypy
- Linting with ruff
Run all tests with:
tox
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Author
Charlie Kim (kimcharli@gmail.com)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ck_recursive_dataclass-0.1.2.tar.gz.
File metadata
- Download URL: ck_recursive_dataclass-0.1.2.tar.gz
- Upload date:
- Size: 7.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b5b7d4c58584d4bde25e0d5dc12b93c99d5394a64864677ec548652607fcc87
|
|
| MD5 |
6822c926fd0cf8f13991a3794cf51603
|
|
| BLAKE2b-256 |
1743e7b268e97693922b89710bf85935838ce6ac583b49b56016f48aeb8539d9
|
File details
Details for the file ck_recursive_dataclass-0.1.2-py3-none-any.whl.
File metadata
- Download URL: ck_recursive_dataclass-0.1.2-py3-none-any.whl
- Upload date:
- Size: 6.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1d517ce82b85d7e6357378798b3ff9195981ecf458774ed6357141a0f37e13a
|
|
| MD5 |
ce11155a8c60f8a940b2e4ae3f0a006c
|
|
| BLAKE2b-256 |
11cc6d4dc1de4248b22631a48b31aa93203595344a9bebca694e82ecde75ad43
|