Skip to content

Commit 1048923

Browse files
committed
fix a couple stubs
1 parent f514d6b commit 1048923

6 files changed

Lines changed: 197 additions & 44 deletions

File tree

pyproject.toml

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@
22
name = "libipld"
33
dynamic = ["version"]
44
description = "Python binding to the Rust IPLD library"
5-
authors = [
6-
{name = "Ilya (Marshal)", email = "ilya@marshal.dev"}
7-
]
5+
authors = [{ name = "Ilya (Marshal)", email = "ilya@marshal.dev" }]
86
license = "MIT"
97
repository = "https://github.com/MarshalX/python-libipld"
108
readme = "README.md"
11-
keywords = ["library", "lib", "ipld", "cid", "multibase", "multihash", "dag", "cbor", "json", "pb", "dag-cbor", "dag-json"]
9+
keywords = [
10+
"library",
11+
"lib",
12+
"ipld",
13+
"cid",
14+
"multibase",
15+
"multihash",
16+
"dag",
17+
"cbor",
18+
"json",
19+
"pb",
20+
"dag-cbor",
21+
"dag-json",
22+
]
1223
requires-python = ">=3.8"
1324
classifiers = [
1425
"Development Status :: 5 - Production/Stable",
@@ -38,12 +49,13 @@ classifiers = [
3849
"Author" = "https://github.com/MarshalX"
3950

4051
[dependency-groups]
41-
dev = ["maturin", "pytest"]
52+
dev = ["maturin", "pytest", "pytest-benchmark"]
4253

4354
[tool.pytest.ini_options]
4455
addopts = [
45-
'--benchmark-columns', 'min,mean,stddev,outliers,rounds,iterations',
46-
'--benchmark-disable', # use --benchmark-enable
56+
'--benchmark-columns',
57+
'min,mean,stddev,outliers,rounds,iterations',
58+
'--benchmark-disable', # use --benchmark-enable
4759
]
4860

4961
[tool.maturin]

pytests/test_decode_car.py

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,27 @@
22

33
import libipld
44
import pytest
5+
from pytest_benchmark.fixture import BenchmarkFixture
56

67
from conftest import load_car_fixture
78

8-
_DID = os.environ.get('CAR_REPO_DID', 'did:plc:w4es6sfh43zlht3bgrzi5qzq') # default is public bot in bsky.app
9-
_REPO_CAR_PATH = os.path.join(os.path.dirname(__file__), '..', 'data', 'repo.car')
9+
_DID = os.environ.get(
10+
"CAR_REPO_DID", "did:plc:w4es6sfh43zlht3bgrzi5qzq"
11+
) # default is public bot in bsky.app
12+
_REPO_CAR_PATH = os.path.join(os.path.dirname(__file__), "..", "data", "repo.car")
1013

1114

12-
@pytest.fixture(scope='session')
15+
@pytest.fixture(scope="session")
1316
def car() -> bytes:
1417
return load_car_fixture(_DID, _REPO_CAR_PATH)
1518

1619

17-
def test_decode_car(benchmark, car) -> None:
20+
def test_decode_car(benchmark: BenchmarkFixture, car: bytes) -> None:
1821
header, blocks = benchmark(libipld.decode_car, car)
1922

20-
assert 1 == header['version']
21-
assert isinstance(header['roots'], list)
22-
assert 1 == len(header['roots'])
23+
assert 1 == header["version"]
24+
assert isinstance(header["roots"], list)
25+
assert 1 == len(header["roots"])
2326

2427
assert isinstance(blocks, dict)
2528
assert all(isinstance(k, bytes) for k in blocks.keys())
@@ -30,71 +33,71 @@ def test_decode_car(benchmark, car) -> None:
3033

3134
def test_decode_car_invalid_header_len() -> None:
3235
with pytest.raises(ValueError) as exc_info:
33-
libipld.decode_car(b'')
36+
libipld.decode_car(b"")
3437

35-
assert 'Invalid uvarint' in str(exc_info.value)
38+
assert "Invalid uvarint" in str(exc_info.value)
3639

3740

3841
def test_decode_car_invalid_header_type() -> None:
3942
with pytest.raises(TypeError) as exc_info:
40-
header_len = bytes.fromhex('33') # 3
41-
header_obj = libipld.encode_dag_cbor('strInsteadOfObj')
43+
header_len = bytes.fromhex("33") # 3
44+
header_obj = libipld.encode_dag_cbor("strInsteadOfObj")
4245
libipld.decode_car(header_len + header_obj)
4346

4447
assert "cannot be converted to 'PyDict'" in str(exc_info.value)
4548

4649

4750
def test_decode_car_invalid_header_version_key() -> None:
4851
with pytest.raises(ValueError) as exc_info:
49-
header_len = bytes.fromhex('33') # 3
50-
header_obj = libipld.encode_dag_cbor({'blabla': 'blabla'})
52+
header_len = bytes.fromhex("33") # 3
53+
header_obj = libipld.encode_dag_cbor({"blabla": "blabla"})
5154
libipld.decode_car(header_len + header_obj)
5255

53-
assert 'Version is None' in str(exc_info.value)
56+
assert "Version is None" in str(exc_info.value)
5457

5558

5659
def test_decode_car_invalid_header_version_value() -> None:
5760
with pytest.raises(ValueError) as exc_info:
58-
header_len = bytes.fromhex('33') # 3
59-
header_obj = libipld.encode_dag_cbor({'version': 2})
61+
header_len = bytes.fromhex("33") # 3
62+
header_obj = libipld.encode_dag_cbor({"version": 2})
6063
libipld.decode_car(header_len + header_obj)
6164

62-
assert 'Version must be 1' in str(exc_info.value)
65+
assert "Version must be 1" in str(exc_info.value)
6366

6467

6568
def test_decode_car_invalid_header_roots_key() -> None:
6669
with pytest.raises(ValueError) as exc_info:
67-
header_len = bytes.fromhex('33') # 3
68-
header_obj = libipld.encode_dag_cbor({'version': 1})
70+
header_len = bytes.fromhex("33") # 3
71+
header_obj = libipld.encode_dag_cbor({"version": 1})
6972
libipld.decode_car(header_len + header_obj)
7073

71-
assert 'Roots is None' in str(exc_info.value)
74+
assert "Roots is None" in str(exc_info.value)
7275

7376

7477
def test_decode_car_invalid_header_roots_value_type() -> None:
7578
with pytest.raises(TypeError) as exc_info:
76-
header_len = bytes.fromhex('33') # 3
77-
header_obj = libipld.encode_dag_cbor({'version': 1, 'roots': 123})
79+
header_len = bytes.fromhex("33") # 3
80+
header_obj = libipld.encode_dag_cbor({"version": 1, "roots": 123})
7881
libipld.decode_car(header_len + header_obj)
7982

8083
assert "cannot be converted to 'PyList'" in str(exc_info.value)
8184

8285

8386
def test_decode_car_invalid_header_roots_value_empty_list() -> None:
8487
with pytest.raises(ValueError) as exc_info:
85-
header_len = bytes.fromhex('33') # 3
86-
header_obj = libipld.encode_dag_cbor({'version': 1, 'roots': []})
88+
header_len = bytes.fromhex("33") # 3
89+
header_obj = libipld.encode_dag_cbor({"version": 1, "roots": []})
8790
libipld.decode_car(header_len + header_obj)
8891

89-
assert 'Roots is empty' in str(exc_info.value)
92+
assert "Roots is empty" in str(exc_info.value)
9093

9194

9295
def test_decode_car_invalid_block_cid() -> None:
9396
with pytest.raises(ValueError) as exc_info:
94-
header_len = bytes.fromhex('33') # 3
95-
header_obj = libipld.encode_dag_cbor({'version': 1, 'roots': ['blabla']})
96-
block1 = bytes.fromhex('33') + b'invalidSid'
97+
header_len = bytes.fromhex("33") # 3
98+
header_obj = libipld.encode_dag_cbor({"version": 1, "roots": ["blabla"]})
99+
block1 = bytes.fromhex("33") + b"invalidSid"
97100

98101
libipld.decode_car(header_len + header_obj + block1)
99102

100-
assert 'Failed to read CID of block' in str(exc_info.value)
103+
assert "Failed to read CID of block" in str(exc_info.value)

python/libipld/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
from ._libipld import (
22
decode_cid,
33
encode_cid,
4+
encode_dag_cbor,
45
decode_car,
56
decode_dag_cbor,
67
decode_dag_cbor_multi,
8+
decode_multibase,
9+
encode_multibase,
710
)
811

912
__all__ = [
1013
"decode_cid",
1114
"encode_cid",
15+
"encode_dag_cbor",
1216
"decode_car",
1317
"decode_dag_cbor",
1418
"decode_dag_cbor_multi",
19+
"decode_multibase",
20+
"encode_multibase",
1521
]
-1.75 MB
Binary file not shown.

python/libipld/_libipld.pyi

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,95 @@
11
from __future__ import annotations
22
from typing import Any
33

4-
def decode_cid(data: str | bytes) -> dict[str, Any]: ...
5-
def encode_cid(data: bytes) -> str: ...
6-
def decode_car(data: bytes) -> tuple[dict[str, Any], dict[bytes, dict[str, Any]]]: ...
7-
def decode_dag_cbor(data: bytes) -> dict[str, Any]: ...
8-
def decode_dag_cbor_multi(data: bytes) -> list[dict[str, Any]]: ...
9-
def encode_dag_cbor(data: dict[str, Any]) -> bytes: ...
10-
def decode_multibase(data: str) -> tuple[str, bytes]: ...
11-
def encode_multibase(code: str, data: str | bytes) -> str: ...
4+
def decode_cid(data: str | bytes) -> dict[str, Any]:
5+
"""Decode a CID from either its string representation or raw bytes.
6+
7+
Args:
8+
data: Either a CID string (e.g. 'bafy...') or raw CID bytes
9+
10+
Returns:
11+
A dict containing:
12+
- version: int (0 or 1)
13+
- codec: int (e.g. 113 for DAG-CBOR)
14+
- hash: dict containing:
15+
- code: int (hash algorithm code)
16+
- size: int (hash size in bytes)
17+
- digest: bytes (hash digest)
18+
"""
19+
20+
def encode_cid(data: str | bytes) -> str:
21+
"""Encode a CID to its string representation.
22+
23+
Args:
24+
data: Either a CID string (will be returned as-is) or raw CID bytes
25+
26+
Returns:
27+
A CID string (e.g. 'bafy...')
28+
"""
29+
30+
def decode_car(data: bytes) -> tuple[dict[str, Any], dict[bytes, dict[str, Any]]]:
31+
"""Decode a CAR file.
32+
33+
Args:
34+
data: Raw CAR file bytes
35+
36+
Returns:
37+
A tuple containing:
38+
- header: dict (CAR header)
39+
- blocks: dict mapping CID bytes to block data
40+
"""
41+
42+
def decode_dag_cbor(data: bytes) -> Any:
43+
"""Decode DAG-CBOR data to Python objects.
44+
45+
Args:
46+
data: Raw DAG-CBOR bytes
47+
48+
Returns:
49+
A Python object (dict, list, str, bytes, int, float, bool, or None)
50+
"""
51+
52+
def decode_dag_cbor_multi(data: bytes) -> list[Any]:
53+
"""Decode multiple DAG-CBOR objects from bytes.
54+
55+
Args:
56+
data: Raw DAG-CBOR bytes containing multiple objects
57+
58+
Returns:
59+
A list of Python objects
60+
"""
61+
62+
def encode_dag_cbor(data: Any) -> bytes:
63+
"""Encode Python objects to DAG-CBOR.
64+
65+
Args:
66+
data: Any Python object that can be encoded to DAG-CBOR
67+
(dict, list, str, bytes, int, float, bool, or None)
68+
69+
Returns:
70+
Raw DAG-CBOR bytes
71+
"""
72+
73+
def decode_multibase(data: str) -> tuple[str, bytes]:
74+
"""Decode multibase-encoded data.
75+
76+
Args:
77+
data: Multibase-encoded string (e.g. 'u' for base58btc)
78+
79+
Returns:
80+
A tuple containing:
81+
- base: str (the base code, e.g. 'u')
82+
- data: bytes (the decoded data)
83+
"""
84+
85+
def encode_multibase(code: str, data: Any) -> str:
86+
"""Encode data using multibase.
87+
88+
Args:
89+
code: str (base code, e.g. 'u' for base58btc)
90+
data: Any Python object that can be converted to bytes
91+
(str, bytes, bytearray, or other objects that can be converted to bytes)
92+
93+
Returns:
94+
Multibase-encoded string
95+
"""

uv.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)