Metadata-Version: 2.1
Name: baozi
Version: 0.0.1
Author: race
Author-email: raceychan@gmail.com
License: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE


# Table of Contents

- [Table of Contents](#table-of-contents)
  - [Introduction](#introduction)
  - [Extract features domino offers](#extract-features-domino-offers)
  - [Rationale of existence](#rationale-of-existence)
    - [why wouldn't I just use dataclass?](#why-wouldnt-i-just-use-dataclass)
    - [why wouldn't I just use pydantic.BaseModel?](#why-wouldnt-i-just-use-pydanticbasemodel)
  - [Installation](#installation)
  - [Usage](#usage)
  - [1. Usage of plain Struct](#1-usage-of-plain-struct)
  - [2. Usage of FrozenStruct](#2-usage-of-frozenstruct)
    - [Domino is fully compatible with dataclasses.dataclass](#domino-is-fully-compatible-with-dataclassesdataclass)
    - [FrozenStruct is equal to dataclass(frozen=True, slots=True, kw\_only=True)](#frozenstruct-is-equal-to-dataclassfrozentrue-slotstrue-kw_onlytrue)
    - [Defining FrozenStruct with mutable fields would raise error](#defining-frozenstruct-with-mutable-fields-would-raise-error)
    - [Technical Details](#technical-details)
      - [config override order](#config-override-order)

## Introduction

domino is a light, enhanced alternative of python's builtin dataclass

## Extract features domino offers

- Inheritance
- Immutability
- Keyword-only arguments and random order attributes
- Support user-defined `__pre_init__` method that gets executed before class instantiation
- Typecasting(experimental)

## Rationale of existence

### why wouldn't I just use dataclass?

Mostly because of **the decorator** approach, dataclass uses decorators for transforming a regular class into a dataclass, and when I use dataclass in production, I soon realize that:

1. Code repetition, when there are tons of dataclasses, you are going to decorate them with `@dataclass` over and over again

2. Error-prone, for non-trivial application, you often have different configuration of dataclass, like `@dataclass(frozen)`, if you inherit from a frozen dataclass and you use regular `@dataclass` decorator, odds are you are going to have a bad time.

3. It simply does not make sense to use decorator, when you decorating a class, it implies that your class would work just fine without the decorator, and that is not the case, if you get rid of the `@dataclass` decorator, your class won't work.

### why wouldn't I just use pydantic.BaseModel?

Mostly because of **You don't need validation**, and you can use both.

pydantic is very powerful and strong and I use it in various projects, but there are
certain scenarios where I find pydantic to be an overkill.

1. **Data are already validated** in the API layer, and once the input goes through API and penetrate into my application layer, i know those data are safe, cause they are generated by my code.

2. **performance**, despite how performant pydantic is, depending on complexity, it is ususally 5x to 100x times slower than a dataclass, which is essentially a regular python class. domino also support usage of slots, which effciently cut about 1/3 of memory usage comparing to regular python class.

## Installation

1. clone the source code to your chosen folder

```bash
mkdir domino && cd domino
git clone git@github.com:raceychan/domino.git
```

2. install the source code from the folder

```bash
pip install -e .
```

3. import domino packages in your project

```python
from domino import Struct
# Your Code
```

## Usage

## 1. Usage of plain Struct

```python
from domino import Struct

class Person(Struct, kw_only=True):
    age: int = 15
    name: str

p = Person(name="domino")
```

> - Note here that attribute with default value does not have to show before regular attributeo
> - You might place dataclass configration directly in class inheritance
> - configs will be passed to subclasses

## 2. Usage of FrozenStruct

### Domino is fully compatible with dataclasses.dataclass

```python
from dataclass import is_dataclass
from domino import FrozenStruct

assert is_dataclass(FrozenStruct)
```

### FrozenStruct is equal to dataclass(frozen=True, slots=True, kw_only=True)

```python
from domino import FrozenStruct, field
from datetime import datetime

class Event(FrozenStruct):
    name: str
    create_at: datetime = field(default_factory=datetime.now)

>> e = Event(name="event")
>> assert isinstance(e.created_at, datetime)
>> e.created_at = datetime.now()

dataclasses.FrozenInstanceError: cannot assign to field 'created_at'
```

### Defining FrozenStruct with mutable fields would raise error

> since any mutable field would cause failure in immutability of the class

```python
from domino import FrozenStruct

class Mutable(FrozenStruct):
    address: list[str]

domino.MutableFieldError
```

### Technical Details

#### config override order

Domino receive config in following order:

1. config defined explicitly using /_/_model_config/_/_ field defined in the class
2. config defined in the inheritance params
3. config defined in the parent class of current class
4. default config of domino, which goes like this:

```python
init=True
repr=True
eq=True
order=False
unsafe_hash=False
frozen=False
match_args=True
kw_only=True
slots=False
```

Struct default:
kw_only=True

FrozenStruct defaults:
kw_only=True
frozen=True
slots=True
