Skip to main content

Lightweight Python library extending dataclasses with serialization, deserialization, validation, and mixins for comparison, hashing, and immutable updates.

Project description

Corbel

Corbel is a Python dataclass extension library providing mixins and utilities for:

  • Validation
  • Comparison and hashing
  • Copying and updating
  • Serialization to/from dicts and JSON
  • Property-level metadata (@corbel_property)
  • Protocol-based hooks for validation, serialization, and deserialization

Installation

pip install corbel

Core Mixins

1. Corbel

The base mixin that provides:

  • Cached asdict results
  • Field and property introspection
  • Hook methods for updates and validation
from corbel import dataclass
from corbel import Corbel, field

@dataclass
class Base(Corbel):
    x: int = field()
    y: str = field()

inst = Base(1, "hello")
print(inst.asdict())  # {'x': 1, 'y': 'hello'}

2. Serializable

Provides to_dict, from_dict, to_json, from_json for dataclasses.

  • Supports nested dataclasses
  • Optional wrapper key for JSON (__json_wrapper__)
  • Configurable inclusion rules (__inclusion__)
from corbel import dataclass
from corbel import Serializable, field, Include

@dataclass
class User(Serializable):
    id: int = field()
    name: str = field()
    email: str | None = field(default=None)

user = User(1, "Alice")
print(user.to_dict())  # {'id': 1, 'name': 'Alice', 'email': None}
alice = User.from_dict({"id": 1, "name": "Alice", "email": None})

# JSON with wrapper
print(user.to_json(wrapper="user"))  # {"user": {"id":1,"name":"Alice","email":null}}
alice = User.from_json('{"user": {"id":1,"name":"Alice","email":null}', wrapper="user")

# Custom class-level JSON wrapper
@dataclass
class WrappedUser(Serializable):
    __json_wrapper__ = "account"
    id: int = field()
    name: str = field()

u = WrappedUser(5, "Bob")
print(u.to_json())  # {"account": {"id":5,"name":"Bob"}}

# Using inclusion rules
@dataclass
class PartialUser(Serializable):
    __inclusion__ = Include.NON_NONE
    id: int = field()
    name: str = field()
    email: str | None = field(default=None)

pu = PartialUser(1, "Alice")
print(pu.to_dict())  # {'id': 1, 'name': 'Alice'}  # email omitted

3. Updatable

Provides immutable-style updates:

  • copy(): shallow copy
  • update(**kwargs): returns a new instance with updated fields
  • batch_update(): context manager to temporarily disable validation
from corbel import dataclass
from corbel import Updatable, field

@dataclass
class Point(Updatable):
    x: int = field()
    y: int = field()

p1 = Point(1, 2)
p2 = p1.update(x=10)  # new instance
print(p1.asdict())    # {'x': 1, 'y': 2}
print(p2.asdict())    # {'x': 10, 'y': 2}

# batch update
with p2.batch_update() as temp:
    temp.x = 20
    temp.y = 30
print(p2.asdict())  # {'x': 10, 'y': 2}, p2 unchanged

4. Validated

Automatically validates fields on initialization and update:

  • Define a validator in field() metadata
  • Supports allow_none=True
  • Raises ValidationError on failure
from corbel import dataclass
from corbel import Validated, field, ValidationError

def positive(value: int) -> bool:
    return value > 0

@dataclass
class BankAccount(Validated):
    balance: int = field(validator=positive)

try:
    acct = BankAccount(-10)  # raises ValidationError
except ValidationError as e:
    print(e)

acct = BankAccount(100)
acct.balance = -50  # raises ValidationError

5. Hashable

Caches a hash based on dataclass fields:

  • Automatically invalidates on field update
  • Suitable for dict keys and set members
from corbel import dataclass
from corbel import Hashable, field

@dataclass
class Coord(Hashable):
    x: int = field()
    y: int = field()

c1 = Coord(1, 2)
c2 = Coord(1, 2)

print(hash(c1) == hash(c2))  # True
c1.x = 3
print(hash(c1) == hash(c2))  # False

6. Comparable

Provides <, <=, >, >=, == based on field values:

  • Lexicographic comparison of fields
  • Supports total ordering
from corbel import dataclass
from corbel import Comparable, field

@dataclass
class Version(Comparable):
    major: int = field()
    minor: int = field()

v1 = Version(1, 0)
v2 = Version(1, 1)
print(v1 < v2)  # True
print(v1 == v2) # False

7. @corbel_property

Custom property decorator supporting:

  • validator
  • serializer / deserializer
  • allow_none / ignore
from corbel import dataclass
from corbel import Corbel, corbel_property, field

def positive(x: int) -> bool:
    return x > 0

@dataclass
class Example(Corbel):
    _value: int = field()

    @corbel_property(validator=lambda v: positive(v))
    def value(self) -> int:
        return self._value

    @value.setter
    def value(self, val: int) -> None:
        self._value = val

ex = Example(5)
print(ex.value)  # 5
ex.value = 10    # OK
# ex.value = -1  # Raises ValueError

8. Protocol Examples

ValidatorProtocol

from typing import Any

def positive_validator(value: int) -> bool:
    return value > 0

print(positive_validator(5))   # True
print(positive_validator(-1))  # False

SerializerProtocol

from typing import Any

def uppercase_serializer(value: Any) -> Any:
    if isinstance(value, str):
        return value.upper()
    return value

print(uppercase_serializer("hello"))  # "HELLO"

DeserializerProtocol

from typing import Any

def deserialize_int(value: Any, type_hint: int) -> int:
    if type_hint == int and isinstance(value, str):
        return int(value)
    return value

print(deserialize_int("42", int))  # 42

9. Combining Mixins

Mixins can be combined for full-featured dataclasses:

from corbel import dataclass
from corbel import Serializable, Updatable, Validated, Hashable, Comparable, field, corbel_property

@dataclass
class Product(Serializable, Updatable, Validated, Hashable, Comparable):
    name: str = field()
    price: float = field()

    @corbel_property()
    def discounted_price(self) -> float:
        return self.price * 0.9

prod = Product("Widget", 100)
prod2 = prod.update(price=120)
print(prod.to_dict())  # {'name': 'Widget', 'price': 100}
print(prod2.discounted_price)  # 108.0

Utilities

  • asdict(obj, include_private=False): convert instance to dict
  • field(**kwargs): wrapper for dataclass fields with Corbel metadata
  • fields(obj): returns dataclass fields
  • Include enum: ALWAYS, NON_NONE, NON_EMPTY, NON_DEFAULT
  • Exceptions: ValidationError, DeserializeError, InclusionError, CorbelError
  • Class-level options for Serializable:
    • __json_wrapper__ – wrap the JSON output under a key
    • __inclusion__ – control which fields are included

License

MIT License. See LICENSE.

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

corbel-0.1.3.tar.gz (28.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

corbel-0.1.3-py3-none-any.whl (45.4 kB view details)

Uploaded Python 3

File details

Details for the file corbel-0.1.3.tar.gz.

File metadata

  • Download URL: corbel-0.1.3.tar.gz
  • Upload date:
  • Size: 28.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.11.5 Darwin/25.0.0

File hashes

Hashes for corbel-0.1.3.tar.gz
Algorithm Hash digest
SHA256 033a3a3a294af74ff9ce7e61832141ab8d8b883dabf1326122e29d1b83d27379
MD5 e07eef1f3143f482a25937619aa0717b
BLAKE2b-256 b67acd8c4d89bd2fdfe9608d4e60801861416914ff76291fa47b519ecfc2ec5b

See more details on using hashes here.

File details

Details for the file corbel-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: corbel-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 45.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.11.5 Darwin/25.0.0

File hashes

Hashes for corbel-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e645a4c12e7b5969b530ad85c547da9910c686763b46c1bda26c30c5c24b77f2
MD5 a614646aa69bd8fc56e315a986097d77
BLAKE2b-256 fd2a2de3ce8537f788d6e090bbe0bf2e51f880068cde0ef0a7e6667f3318c244

See more details on using hashes here.

Supported by

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