Metadata-Version: 2.4
Name: no-value
Version: 1.0.0
Summary: Sentinel value for missing data where None has its own meaning
Project-URL: Homepage, https://github.com/miriada-io/no-value
Project-URL: Repository, https://github.com/miriada-io/no-value
Project-URL: Issues, https://github.com/miriada-io/no-value/issues
Project-URL: Changelog, https://github.com/miriada-io/no-value/blob/master/CHANGELOG.md
Author-email: Miriada <info@miriada.io>
License-Expression: MIT
License-File: LICENSE
Keywords: missing,none,null,sentinel,sentinel-value,undefined
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: coverage>=7.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# no-value

[![PyPI](https://img.shields.io/pypi/v/no-value.svg?label=PyPI)](https://pypi.org/project/no-value/)
[![Python](https://img.shields.io/pypi/pyversions/no-value.svg?label=Python)](https://pypi.org/project/no-value/)
[![Tests](https://github.com/miriada-io/no-value/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/miriada-io/no-value/actions/workflows/tests.yml)
[![License](https://img.shields.io/pypi/l/no-value.svg?label=License)](https://github.com/miriada-io/no-value/blob/master/LICENSE)

Sentinel value to express missing keys or values where `None` has its own meaning.

## Why?

When building APIs or update operations, you often need to distinguish between:
- **a value** — set the field
- **`None`** — clear the field
- **not provided** — leave the field unchanged

Python's `None` can't cover both "clear" and "not provided". `NoValue` fills that gap.

## Installation

```bash
pip install no-value
```

## Usage

```python
from no_value import NoValue


def update_user(name: str | None | NoValue = NoValue) -> None:
    if name is NoValue:
        pass  # not provided, do nothing
    elif name is None:
        clear_name()  # explicitly set to null
    else:
        set_name(name)  # update with new value
```

## Behavior

- Use `is` for comparison: `val is NoValue`
- `str(NoValue)` and `repr(NoValue)` return `"NoValue"`
- `NoValue()` raises `TypeError` — it cannot be instantiated
- `bool(NoValue)` raises `TypeError` — to prevent accidental truthy/falsy checks

## Limitations

Static analysis tools (mypy, pyright) do not fully support custom sentinel types. Type annotations like `str | None | NoValue` work at runtime but may produce warnings in strict mode.

## License

[MIT](https://github.com/miriada-io/no-value/blob/master/LICENSE)
