Metadata-Version: 2.4
Name: chaingang
Version: 0.1.0
Summary: Python class decorator that adds selection chaining
Author: Odos Matthews
License-Expression: MIT
Project-URL: Homepage, https://github.com/eddiethedean/chaingang
Project-URL: Repository, https://github.com/eddiethedean/chaingang
Project-URL: Issues, https://github.com/eddiethedean/chaingang/issues
Project-URL: Changelog, https://github.com/eddiethedean/chaingang/blob/main/CHANGELOG.md
Keywords: decorator,chaining,getitem,nested
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: testing
Requires-Dist: mypy>=1.13; extra == "testing"
Requires-Dist: ruff>=0.8; extra == "testing"
Requires-Dist: flake8>=7; extra == "testing"
Requires-Dist: tox>=4; extra == "testing"
Requires-Dist: pytest>=8; extra == "testing"
Requires-Dist: pytest-cov>=5; extra == "testing"
Dynamic: license-file

# ChainGang

**Tuple-style indexing for nested lists** — `obj[i, j, k]` means `obj[i][j][k]` for get, set, and delete.

[![PyPI version](https://img.shields.io/pypi/v/chaingang.svg)](https://pypi.org/project/chaingang/)
[![Python versions](https://img.shields.io/pypi/pyversions/chaingang.svg)](https://pypi.org/project/chaingang/)
[![Tests](https://github.com/eddiethedean/chaingang/actions/workflows/tests.yml/badge.svg)](https://github.com/eddiethedean/chaingang/actions/workflows/tests.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/eddiethedean/chaingang/blob/main/LICENSE)

## Overview

ChainGang exposes small **class decorators** that wrap `__getitem__`, `__setitem__`, and `__delitem__` so that a **tuple** key walks nested containers in one step. Non-tuple keys (single indexes, slices) are passed through to the base type unchanged.

| Decorator | Effect |
|-----------|--------|
| `selection_chaining` | Chained get, set, and delete |
| `getitem_chaining` | Chained read only |
| `setitem_chaining` | Chained write only |
| `delitem_chaining` | Chained delete only |

**Python 3.10+.** No runtime dependencies beyond the standard library.

## Install

```sh
pip install chaingang
```

Install from source:

```sh
git clone https://github.com/eddiethedean/chaingang.git
cd chaingang
pip install .
```

Optional dev tools: `pip install chaingang[testing]`

## Usage

Apply `@selection_chaining` to a class that already supports nested indexing (typically a `list` subclass). Then use comma-separated indices anywhere you would otherwise chain brackets.

```python
from chaingang import selection_chaining


@selection_chaining
class ChainList(list):
    """Nested list with tuple indexing."""

    pass


cl = ChainList([[1, 2, 3], [4, 5, 6]])

# Read: tuple index equals chained brackets
assert cl[1, 2] == 6
assert cl[1][2] == 6

# Write
cl[1, 2] = 100
assert cl == [[1, 2, 3], [4, 5, 100]]

# Delete (here, remove the middle element of the inner row)
del cl[1, 1]
assert cl == [[1, 2, 3], [4, 100]]
```

You can compose the lower-level decorators if you only need one protocol, for example:

```python
from chaingang.selection_chaining import getitem_chaining

ReadOnlyChain = getitem_chaining(list)
```

Import the combined decorator from the package root or from `chaingang.selection_chaining`.

## Tuple keys and compatibility

In Python, `obj[a, b]` always passes `key` as the tuple `(a, b)`. These decorators **always** treat a tuple key as a **chain** of lookups or updates. That matches nested **lists** (and similar sequence-like types).

**Avoid** using the decorators on types where a tuple is already a **single** valid key (for example `dict` with tuple keys, or NumPy-style indexing). Tuple keys are reserved for chaining and will not match the base mapping or array semantics.

An **empty** tuple `()` is rejected with `TypeError` (it would otherwise be meaningless for set/delete).

## Type checkers

The package ships `py.typed` (PEP 561). Point mypy, Pyright, or other tools at your environment after `pip install chaingang`.

## Development

```sh
pip install -r requirements_dev.txt
pytest
# or full lint + tests as in CI:
tox
```

## Changelog and links

- [CHANGELOG.md](https://github.com/eddiethedean/chaingang/blob/main/CHANGELOG.md)
- [Repository](https://github.com/eddiethedean/chaingang)
- [Issue tracker](https://github.com/eddiethedean/chaingang/issues)
- [PyPI](https://pypi.org/project/chaingang/)

## License

MIT — see [LICENSE](https://github.com/eddiethedean/chaingang/blob/main/LICENSE).
