convclasses: старая песня на новый лад

Уже больше года мы на своем проекте используем отличный инструмент cattrs. Он показался мне на столько классным, что я даже написал еще один инструмент на его основе — apitist (о нем я расскажу как-нибудь позже). Инструмент позволяет с легкостью работать с моделями данных — дает возможность преобразовывать из словарей и списков данные в объекты моделей и обратно.

Под капотом cattrs использует библиотеку attrs. Она в свою очередь позволяет быстро делать классы:

>>> @attr.s
... class Coordinates(object):
...     x = attr.ib()
...     y = attr.ib()
>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False

Ну, не то чтобы прямо под капотом, скорее cattrs умеет структурировать и деструктурировать данные из attrs классов. Благодаря этому cattrs позволяет работать на Python версии 2.7 и >3.5.

В Python 3.7 появился похожий функционал в стандартной библиотеке — dataclasses. А благодаря ericvsmith этот функционал был перенесен на Python 3.6.

Мне было интересно и я решил перенести функционал cattrs на dataclasses. Результатом стала библиотека convclasses, которая позволяет делать так:

from enum import unique, Enum
from typing import Any, List, Optional, Sequence, Union
from convclasses import structure, unstructure
from dataclasses import dataclass

@unique
class CatBreed(Enum):
    SIAMESE = "siamese"
    MAINE_COON = "maine_coon"
    SACRED_BIRMAN = "birman"

@dataclass
class Cat:
    breed: CatBreed
    names: Sequence[str]

@dataclass
class DogMicrochip:
    chip_id: Any
    time_chipped: float

@dataclass
class Dog:
    cuteness: int
    chip: Optional[DogMicrochip]

p = unstructure([
	Dog(cuteness=1, chip=DogMicrochip(chip_id=1, time_chipped=10.0)),
    Cat(breed=CatBreed.MAINE_COON, names=('Fluffly', 'Fluffer'))
])

print(p)
# [{'cuteness': 1, 'chip': {'chip_id': 1, 'time_chipped': 10.0}}, {'breed': 'maine_coon', 'names': ('Fluffly', 'Fluffer')}]
print(structure(p, List[Union[Dog, Cat]]))
# [Dog(cuteness=1, chip=DogMicrochip(chip_id=1, time_chipped=10.0)), Cat(breed=<CatBreed.MAINE_COON: 'maine_coon'>, names=['Fluffly', 'Fluffer'])]

Благодаря этим библиотекам можно быстро и просто производить тестирование REST API использующее JSON.

Более подробно расскажу как-нибудь потом, а на этом анонс alles.