Make pydantic have a GraphQL-like assembly experience.
Project description
Pydantic-resolve
import asyncio
from pydantic import BaseModel
from pydantic_resolve import resolve
class Book(BaseModel):
name: str
class Student(BaseModel):
name: str
greet: str = ''
async def resolve_greet(self):
await asyncio.sleep(1) # mock i/o
return f'hello {self.name}'
async def main():
students = [Student(name='john' )]
results = await resolve(students)
print(results)
asyncio.run(main())
# output: [Student(name='john', greet='hello john')]
-
Pydantic-resolve helps you asynchoronously, resursively resolve a pydantic object (or dataclass object)
-
Pydantic-resolve, when used in conjunction with aiodataloader, allows you to easily generate nested data structures without worrying about generating N+1 queries.
Install
pip install pydantic-resolve
Basic Demo
from pydantic_resolve import resolve
class Book(BaseModel):
name: str
class Student(BaseModel):
name: str
intro: str = ''
def resolve_intro(self):
return f'hello {self.name}'
books: tuple[Book, ...] = tuple()
async def resolve_books(self):
return await get_books()
async def get_books():
await asyncio.sleep(1)
return [Book(name="sky"), Book(name="sea")]
class TestResolver(unittest.IsolatedAsyncioTestCase):
async def test_resolver_1(self):
stu = Student(name="boy")
result = await resolve(stu)
expected = {
'name': 'boy',
'intro': 'hello boy',
'books': [{'name': 'sky'}, {'name': 'sea'}]
}
self.assertEqual(result.dict(), expected)
async def test_resolver_2(self):
stu = [Student(name="boy")]
result = await resolve(stu)
expected = {
'name': 'boy',
'intro': 'hello boy',
'books': [{'name': 'sky'}, {'name': 'sea'}]
}
self.assertEqual(result[0].dict(), expected)
async def test_schema(self):
# Student.update_forward_refs(Book=Book)
schema = Student.schema_json()
expected = '''{"title": "Student", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "intro": {"title": "Intro", "default": "", "type": "string"}, "books": {"title": "Books", "default": [], "type": "array", "items": {"$ref": "#/definitions/Book"}}}, "required": ["name"], "definitions": {"Book": {"title": "Book", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}, "required": ["name"]}}}'''
self.assertEqual(schema, expected)
Demo: Integrated with aiodataloader:
from __future__ import annotations
from typing import Tuple
import unittest
from pydantic import BaseModel
from pydantic_resolve import resolve
from aiodataloader import DataLoader
class TestDataloaderResolver(unittest.IsolatedAsyncioTestCase):
async def test_dataloader_1(self):
BOOKS = {
1: [{'name': 'book1'}, {'name': 'book2'}],
2: [{'name': 'book3'}, {'name': 'book4'}],
}
class Book(BaseModel):
name: str
class BookLoader(DataLoader):
async def batch_load_fn(self, keys):
books = [[Book(**bb) for bb in BOOKS.get(k, [])] for k in keys]
return books
book_loader = BookLoader()
class Student(BaseModel):
id: int
name: str
books: Tuple[Book, ...] = tuple()
def resolve_books(self):
return book_loader.load(self.id)
students = [Student(id=1, name="jack"), Student(id=2, name="mike")]
results = await resolve(students)
source = [r.dict() for r in results]
expected = [
{'id': 1, 'name': 'jack', 'books': [{ 'name': 'book1'}, {'name': 'book2'}]},
{'id': 2, 'name': 'mike', 'books': [{ 'name': 'book3'}, {'name': 'book4'}]},
]
self.assertEqual(source, expected)
Unittest
poetry run python -m unittest # or
poetry run pytest # or
poetry run tox
Coverage
poetry run coverage run -m pytest
poetry run coverage report -m
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
pydantic_resolve-0.2.1.tar.gz
(4.0 kB
view hashes)
Built Distribution
Close
Hashes for pydantic_resolve-0.2.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 025212b9e9a74d47201817c4d347bf7f5c9e9eb500bc37d1f51c7af5518ba19b |
|
MD5 | f20a86e61d67f8be3967fbbb7e9de8c2 |
|
BLAKE2b-256 | 0be0b28e4f908fd2b4ff51aba5a11910f2cc2e1b5c0cdb27567d9f181f58b753 |