An open API service indexing awesome lists of open source software.

https://github.com/oodesigns/constrained-values

A Python library for creating type-safe, self-validating value objects using a powerful transformation and validation pipeline.
https://github.com/oodesigns/constrained-values

constrained-enum constrained-range constrained-value constraint-programming python validated-enum validated-range validated-value validation validation-library validator value-object

Last synced: 22 days ago
JSON representation

A Python library for creating type-safe, self-validating value objects using a powerful transformation and validation pipeline.

Awesome Lists containing this project

README

          

[![Docs](https://img.shields.io/badge/docs-latest-brightgreen.svg?logo=readthedocs&logoColor=white)](https://OODesigns.github.io/constrained-values/constrained_values.html)
[![Build Status](https://github.com/oodesigns/constrained-values/actions/workflows/website.yml/badge.svg)](https://github.com/OODesigns/constrained-values/actions)
[![PyPI Version](https://img.shields.io/pypi/v/constrained-values.svg?logo=pypi&logoColor=white)](https://pypi.org/project/constrained-values/)
![Python Versions](https://img.shields.io/pypi/pyversions/constrained-values.svg)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)

## Constrained Values

A lightweight Python library for **creating type-safe, self-validating value objects** โ€” transforming primitive data into meaningful, domain-aware objects with rich validation and transformation pipelines.

---

## ๐Ÿงญ Philosophy: Beyond Primitive Types

In most codebases, we pass around raw values without context:

- Is `temperature = 25` Celsius or Fahrenheit?
- Is `spi_mode = 2` valid for this device?
- What does `-32768` mean again?

Primitive values lack **meaning**, **constraints**, and **domain intent**.
This is *Primitive Obsession* โ€” a subtle but pervasive design smell.

**Constrained Values** replaces primitives with expressive, validated objects that make **validity explicit**.
Each object carries its **status** (`OK` or `EXCEPTION`) and associated errors.

By default, invalid values can exist safely and report their state โ€” but if you want to enforce strict invariants, you can enable **exception mode** to raise immediately on invalid input.

๐Ÿ“– [**Full Documentation โ†’**](https://oodesigns.github.io/constrained-values/constrained_values.html#the-philosophy-beyond-primitive-types)

---

## โœจ Features

- ๐Ÿงฉ **Rich Value Objects** โ€“ Replace primitives with expressive, validated domain objects.
- ๐Ÿ”— **Composable Pipelines** โ€“ Chain multiple validation and transformation strategies.
- ๐Ÿง  **Built-in Validators** โ€“ Range checks, enums, type coercion, and more.
- โš™๏ธ **Custom Logic** โ€“ Easily extend with your own domain-specific rules.
- ๐Ÿšฆ **Clear Error Handling** โ€“ Track validation status and descriptive messages.
- ๐Ÿงฏ **Strict/Exception Mode (optional)** โ€“ By default, invalid values are reported non-destructively; enable strict mode to raise exceptions and enforce invariants at creation.
- ๐Ÿงพ **Type-Safety** โ€“ Each value enforces its canonical type at runtime.

---

## ๐Ÿš€ Installation

```bash
pip install constrained-values
```
## ๐Ÿ’ก Quick Example

```python
from constrained_values import RangeValue

class Temperature(RangeValue):
"""Temperature constrained between 0 and 100ยฐC."""
def __init__(self, value):
super().__init__(value, low_value=0, high_value=100)

def main() -> None:
# โœ… Valid temperature
t = Temperature(42)
print("Value:", t.value) # 42
print("Status:", t.status.name) # OK
print("Details:", t.details) # validation successful

# ๐Ÿšซ Invalid temperature
t_invalid = Temperature(120)
print("Value:", t_invalid.value) # None
print("Status:", t_invalid.status) # Status.EXCEPTION
print("Details:", t_invalid.details) # Value must be less than or equal to 100, got 120

if __name__ == "__main__":
main()
```
## ๐Ÿ”ฅ Strict version
If you want the behavior where invalid input throws immediately, use the StrictValue mixin:
```python
class StrictTemperature(RangeValue, StrictValue):
"""Same range constraint but raises if invalid."""
def __init__(self, value):
RangeValue.__init__(self, value, low_value=0, high_value=100)

def main() -> None:
t = StrictTemperature(42)
print("Value:", t.value) # 42

try:
StrictTemperature(120)
except ValueError as e:
print(e) # Failed Constraints for value - '120': Value must be less than or equal to 100, got 120

if __name__ == "__main__":
main()
```