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 dataclasses 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 dataclasses 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}

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

# 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 dataclasses 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 dataclasses 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 dataclasses 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 dataclasses 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 dataclasses 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
    def value(self) -> int:
        return self._value

    @value.setter
    def value(self, val: int) -> None:
        if not positive(val):
            raise ValueError("Must be positive")
        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 dataclasses 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.0.tar.gz (69.4 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.0-py3-none-any.whl (44.8 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for corbel-0.1.0.tar.gz
Algorithm Hash digest
SHA256 614ebb6db2a7f5d3493f771749572411c877b64e2855e444e37663cdd669c8cd
MD5 06e5c35c5adeca28f18c9844f377b3f8
BLAKE2b-256 ef46d9ee65acde5d9d334aa35d753ed9576d4e789660d3ab105f4d74ae9f1151

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for corbel-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 55d0dd553eb8d338a387a5a900718198c1962e20cad9f628fe496235342e4e27
MD5 f82935589f43f7234520cd39db7c4d6f
BLAKE2b-256 1c86f1452ae275fdcf5f797483edbcd33bb6fe8bc3cb04397b31be32a2504a9b

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