Skip to main content

Serialize/deserialize Python objects from/to typed structures.

Project description

serdelicacy

image-version image-license image image-ci readthedocs-status

Serialize (serdelicacy.dump) and deserialize (serdelicacy.load) from/to strongly-typed, native Python data structures.

Read the latest documentation here

Features

  1. Effortless deserialization of unstructured Python types into structured, type-hinted Python types (dataclasses.dataclass, typing.NamedTuple)
  2. Effortless serialization of structured, type-hinted Python objects into unstructured Python types (eg, the reverse)
  3. Clear error messages when serde fails at runtime
  4. No inherited, non-standard types. dataclasses, NamedTuples, and other standard Python types are bread and butter
  5. Editor support: I like my autocompletion, so I jump through lots of hoops to make this library compatible with Jedi
  6. Handle optional properties with a domain-specific serdelicacy.OptionalProperty
  7. Enable customization through sophisticated validation, deserialization overrides, and serialization overrides for dataclasses.
  8. Require no 3rd party dependencies; Python 3.8+

Installation

# With pip
pip install serdelicacy

# With poetry
poetry add serdelicacy

Usage

See examples folder.

Validation / transformation for dataclasses

Customization override options are available for validations and transformations on both deserialization and serialization. Custom overrides are available for dataclasses through the metadata argument to the dataclasses.field function:

from dataclasses import dataclass, field

import serdelicacy
from serdelicacy import Override

def _is_long_enough(value) -> None:
    if len(value) < 4:
        raise ValueError(f"'{value}' is not enough characters")

VALUE = {"firstname": "richard", "lastname": "spencerson"}

@dataclass
class Person:
    firstname: str = field(
        metadata={
            "serdelicacy": Override(
                validate=_is_long_enough,
                transform_load=str.title,
            )
        }
    )
    lastname: str = field(
        metadata={
            "serdelicacy": Override(
                validate=_is_long_enough,
                transform_load=str.title,
                transform_dump=str.upper,
            )
        }
    )

print(serdelicacy.load(VALUE, Person))

As suggested by the Python dataclasses.field documentation, all serdelicacy-related field metadata is namespaced to 1 dictionary key: serdelicacy. Its value should be of type serdelicacy.Override, a dataclass itself whose fields are the following:

  • validate: Callable[[Any], NoReturn], Callable[[Any], bool]: a function that either a) returns a boolean where False indicates failed validation or b) nothing, but raises Python exceptions on validation failure. Is executed as the final step of a value's load, after all transformations have been completed. By default, this is a function that does nothing.
  • transform_load: Callable[[Any], Any]. This transformation is executed before any other loading takes place. By default, this is an identity function
  • transform_postload: this should be Callable[[T], T]], where T is the type of the field. This transformation is executed after all recursive loading takes place as the final step before the value is returned for upstream processing. By default, this is an identity function
  • transform_dump: this should be Callable[[T], Any]], where T is the type of the field. This function is executed before a value is recursively serialized. By default, this is an identity function

Finally, you may not need to use these tools initially, but if you have strict validation or transformation requirements on your project, you'll be extremely happy they're here

FAQ

My JSON keys contain whitespace, etc

Simple solution: use typeing.TypeDict's backwards-compatibility syntax.

from pprint import pprint
from typing import List, TypedDict

import serdelicacy
from serdelicacy import OptionalProperty

DATA = [
    {
        "weird, key": 1,
        "normal": 2,
    },
    {
        "normal": 3,
    },
]

DataItem = TypedDict(
    "DataItem",
    {
        "weird, key": OptionalProperty[int],
        "normal": int,
    },
)

LOADED = serdelicacy.load(DATA, List[DataItem])

print("Loaded data:")
pprint(LOADED)

print("Re-serialized data:")
pprint(serdelicacy.dump(LOADED))

This prints the following to the console.

Loaded data:
[{'normal': 2, 'weird, key': 1},
 {'normal': 3, 'weird, key': <Missing property>}]
Re-serialized data:
[{'normal': 2, 'weird, key': 1}, {'normal': 3}]

Try changing values in your JSON data; you'll get runtime errors if your data does not conform to the above schema. Additionally, mypy should call out any misused variable keys / types. In short, this has enabled a type-safe load and a perfectly sane dump.

Local Development

Local development for this project is simple.

Dependencies

Install the following tools manually.

Recommended

Set up development environment

make setup

Run Tests

make test

Notes

  • Initially inspired by undictify and a PR I helped with. serdelicacy's goals are different; it's focused on serde instead of general function signature overrides.
  • I also notice some striking similarities with a library called typedload (great minds think alike, I guess :p). I renamed my top-level functions to "load" and "dump" in typedload's homage. Unfortunately, as of version 1.20, typedload does not handle all types of dataclasses elegantly (mainly, InitVar). Since typedload supports Python 3.5+, it never will elegantly handle all dataclasses without lots of unfortunate conditionals in the codebase. If you must use Python 3.7-, I suggest looking into typedload.

Written by

Samuel Roeca samuel.roeca@gmail.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

serdelicacy-0.18.1.tar.gz (16.5 kB view details)

Uploaded Source

Built Distribution

serdelicacy-0.18.1-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

Details for the file serdelicacy-0.18.1.tar.gz.

File metadata

  • Download URL: serdelicacy-0.18.1.tar.gz
  • Upload date:
  • Size: 16.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.4 CPython/3.8.6 Linux/5.4.0-56-generic

File hashes

Hashes for serdelicacy-0.18.1.tar.gz
Algorithm Hash digest
SHA256 8ce9f3ed2195fd45b44ebd0328410cfddfb1a26fed55c28bc97d47216f17ee0c
MD5 18c3275c9811a1cab012af8b3bba6c3b
BLAKE2b-256 51dd401abae91ff298cabc8291689cabead082e8c8bc445f5738c1b4eb7f52cf

See more details on using hashes here.

File details

Details for the file serdelicacy-0.18.1-py3-none-any.whl.

File metadata

  • Download URL: serdelicacy-0.18.1-py3-none-any.whl
  • Upload date:
  • Size: 15.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.4 CPython/3.8.6 Linux/5.4.0-56-generic

File hashes

Hashes for serdelicacy-0.18.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4ca62a65db10538da2e94940e53868584e69c1732bc11ed633357d759ba7bff0
MD5 058a3f92650b69a6839ca43391f5ff4e
BLAKE2b-256 2e9efb541ae132999675971e0b1f8ed82f544734db9f82dc346e344aa2e5608c

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page