Metadata-Version: 2.4
Name: libipld
Version: 3.5.0
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
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: Programming Language :: Python :: 3.15
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Pre-processors
License-File: LICENSE
Summary: Python binding to the Rust IPLD library
Keywords: ipld,cid,car,multibase,multihash,dag,cbor,dag-cbor,atproto,bluesky,rust
Author-email: "Ilya (Marshal)" <ilya@marshal.dev>
License-Expression: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Author, https://github.com/MarshalX
Project-URL: Changelog, https://github.com/MarshalX/python-libipld/blob/main/CHANGES.md
Project-URL: Homepage, https://github.com/MarshalX/python-libipld
Project-URL: Repository, https://github.com/MarshalX/python-libipld
Project-URL: Tracker, https://github.com/MarshalX/python-libipld/issues

## Python IPLD

> This project aims to speed up [The AT Protocol SDK](https://github.com/MarshalX/atproto) by using Rust for the heavy lifting. Only atproto related parts are implemented first.

Code snippet:

```python
import libipld

# CID
print(libipld.decode_cid('bafyreig7jbijxpn4lfhvnvyuwf5u5jyhd7begxwyiqe7ingwxycjdqjjoa'))
# Output: {'hash': {'size': 32, 'code': 18, 'digest': b'\xdfHP\x9b\xbd\xbcYOV\xd7\x14\xb1{N\xa7\x07\x1f\xc2C^\xd8D\t\xf44\xd6\xbe\x04\x91\xc1)p'}, 'version': 1, 'codec': 113}
print(libipld.encode_cid(bytes.fromhex('01711220b6811a1d7f8c1791da6d1b4f136dc0e22679eafeaa58d64d7e2faad5890e9d9c')))
# Output: bafyreifwqenb274mc6i5u3i3j4jw3qhcez46v7vkldle27rpvlkysdu5tq

# DAG-CBOR
print(libipld.decode_dag_cbor(b'\xa2aa\x0cabfhello!'))
# Output: {'a': 12, 'b': 'hello!'}
print(libipld.encode_dag_cbor({'a': 12, 'b': 'hello!'}))
# Output: b'\xa2aa\x0cabfhello!'

# multibase
print(libipld.decode_multibase('ueWVzIG1hbmkgIQ'))
# Output: ('u', b'yes mani !')
print(libipld.encode_multibase('u', b'yes mani !'))
# Output: ueWVzIG1hbmkgIQ
```

### Features

#### 🔗 CID (Content Identifier) Operations
- **`decode_cid(data: str | bytes) -> dict[str, Any]`** - Decode CIDs from string representation (e.g., `'bafy...'`) or raw bytes into structured data containing version, codec, and hash information
- **`encode_cid(data: str | bytes) -> str`** - Encode CID raw bytes to string representation, or return string CIDs as-is

#### 📦 DAG-CBOR (Directed Acyclic Graph CBOR) Operations  
- **`decode_dag_cbor(data: bytes) -> Any`** - Decode DAG-CBOR binary data into Python objects (dicts, lists, primitives)
- **`decode_dag_cbor_multi(data: bytes) -> list[Any]`** - Decode multiple concatenated DAG-CBOR objects from a single byte stream
- **`encode_dag_cbor(data: Any) -> bytes`** - Encode Python objects into DAG-CBOR binary format

#### 🌐 Multibase Operations
- **`decode_multibase(data: str) -> tuple[str, bytes]`** - Decode multibase-encoded strings, returning the base identifier and decoded data
- **`encode_multibase(code: str, data: str | bytes) -> str`** - Encode data using specified multibase encoding (e.g. `'u'` for base64url)

#### 🚗 CAR (Content Addressable Archives) Operations
- **`decode_car(data: bytes) -> tuple[dict[str, Any], dict[bytes, dict[str, Any]]]`** - Decode CAR files into header metadata and a mapping of CID bytes to block data

### Error handling

Every function raises `ValueError` for input it cannot decode or encode. The message starts with the failing operation (for example `Failed to read CAR block.`) followed by the reason.

The DAG-CBOR and CAR functions additionally raise `RecursionError` when the data is nested deeper than `sys.getrecursionlimit()`.

Inputs are validated strictly against the specifications. There is no option to skip validation or accept partially broken data. Data produced by third-party encoders is not always valid, so consumers of untrusted input (for example the AT Protocol firehose) should be prepared to catch `ValueError` per message and skip it.

```python
import libipld

try:
    header, blocks = libipld.decode_car(data)
except ValueError as e:
    print(f'skipping malformed CAR: {e}')
except RecursionError:
    print('skipping CAR with excessively nested block')
```

Passing a value of the wrong type, such as a `str` where `bytes` is expected, raises `TypeError`.

### Requirements

- Python 3.8 or higher.

Prebuilt wheels are published for CPython 3.8-3.15, free-threaded CPython 3.14t and 3.15t, and PyPy 3.11.

### Installing

You can install or upgrade `libipld` via

```bash
pip install -U libipld
```

### Performance

Benchmarks against [`cbrrr`](https://github.com/DavidBuchanan314/dag-cbrrr) (C), [`py-ipld-dag`](https://github.com/ipld/py-ipld-dag) (Python wrapper over Rust-backed [`cbor2`](https://github.com/agronholm/cbor2)) and [`dag_cbor`](https://github.com/hashberg-io/dag-cbor) (pure Python), measured on the four classic [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark) fixtures (round-tripped through DAG-CBOR). Bars are operations/second relative to pure-Python `dag_cbor`; higher is better.

Measured on Apple M1, macOS 27, CPython 3.14.0, `libipld` installed from PyPI (PGO + LTO wheel).

#### Deserialization

![deserialization](https://raw.githubusercontent.com/MarshalX/python-libipld/main/benchmark/deserialization.png)

#### Serialization

![serialization](https://raw.githubusercontent.com/MarshalX/python-libipld/main/benchmark/serialization.png)

Reproduce locally:

```bash
cd benchmark && ./run.sh
```

See [`benchmark/README.md`](./benchmark/README.md) for details.

### Changelog

See [`CHANGES.md`](./CHANGES.md) for release notes.

### Contributing

Contributions of all sizes are welcome. See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for setup, build, test, and benchmarking workflow.

### License

MIT – see [LICENSE](./LICENSE).

