About middle

middle is a library created to provide a pythonic way to describe your data structures in the most simple way by using typing hints (from PEP 484), not only providing better readability but also all the necessary boilerplate to create objects based on your data structures, validate and customize input and output.

middle is fast. Lightning fast. It can be used with your favourite web framework, background application, configuration parser or any other usage for highly customized input and/or output of primitive values.

Important

middle was designed with typing hints in mind. It’ll not work if no type is provided within your classes!

What to expect

middle stands on the shoulders of attrs and aims to be as simple as possible to get data from complex objects to Python primitives and vice-versa.

As for “primitives”, you can expect to have simple Python values (like list, dict, str, int, Enum and others) to convert from and to complex objects, containing even other nested objects from any type you can imagine (do not forget to read how to extend middle later on).

Another feature you can expect from middle is it speed. middle can convert from and to objects in a fraction of time compared to some of his “nearest” relatives, which makes it perfect if you need to handle lots of data. But you can use middle with any use case you (my dear friend developer) find fit, because it will do the job just fine.

Benchmarks

(Sneaky) author took a copy of pydantic benchmark suite and created a benchmark file for middle (mostly illustrative):

import typing
from datetime import datetime

import middle


class TestMiddle:
    package = "middle"

    def __init__(self, allow_extra):
        class Location(middle.Model):
            latitude: float = middle.field(default=None)
            longitude: float = middle.field(default=None)

        class Skill(middle.Model):
            subject: str = middle.field()
            subject_id: int = middle.field()
            category: str = middle.field()
            qual_level: str = middle.field()
            qual_level_id: int = middle.field()
            qual_level_ranking: float = middle.field(default=0)

        class Model(middle.Model):
            id: int = middle.field()
            client_name: str = middle.field(max_length=255)
            sort_index: float = middle.field()
            grecaptcha_response: str = middle.field(
                min_length=20, max_length=1000
            )
            client_phone: str = middle.field(max_length=255, default=None)
            location: Location = middle.field(default=None)
            contractor: int = middle.field(minimum=1, default=None)
            upstream_http_referrer: str = middle.field(
                max_length=1023, default=None
            )
            last_updated: datetime = middle.field(default=None)
            skills: typing.List[Skill] = middle.field(default=[])

        self.model = Model

    def validate(self, data):
        try:
            return True, self.model(**data)
        except Exception as e:
            return False, str(e)

I must say that the results were really encouraging considering this is pure Python code (on an old AMD machine), no Cython, no Jit:

Framework Comparison avg/iter stdev/iter
middle (alpha)   39.1μs 0.186μs
pydantic 1.6x slower 62.4μs 0.278μs
toasted-marshmallow 1.7x slower 64.9μs 0.352μs
marshmallow 2.0x slower 79.3μs 0.137μs
trafaret 2.5x slower 97.7μs 1.586μs
django-restful-framework 17.0x slower 662.8μs 1.649μs

Warning

Keep in mind that middle is still in alpha stage. This benchmark will must likely change as middle evolves, since there are some features missing yet (to get to version 1.0.0).