Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ permissions:
env:
UV_FROZEN: true
UV_PYTHON: 3.14 # use the latest version of Python because it is faster
RUST_VERSION: "1.87.0"
RUST_VERSION: "1.90.0"

jobs:
benchmarks:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ permissions:
contents: read

env:
RUST_VERSION: "1.87.0"
RUST_VERSION: "1.90.0"

jobs:
build:
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ name = "libipld"
crate-type = ["rlib", "cdylib"]

[dependencies]
pyo3 = { version = "0.25.1", features = ["generate-import-lib", "anyhow"] }
pyo3 = { version = "0.27.1", features = ["generate-import-lib", "anyhow"] }
python3-dll-a = "0.2.14"
anyhow = "1.0.95"
anyhow = "1.0.100"
libipld = { version = "0.16.0", features = ["dag-cbor"] }
multibase = "0.9.1"
multibase = "0.9.2"
byteorder = "1.5.0"
multihash = "0.18.1"

Expand Down
2 changes: 1 addition & 1 deletion profiling/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ structopt = "0.3.26"
clap = "4.5.29"

[dependencies.pyo3]
version = "0.25.1"
version = "0.27.1"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ testing = [
]
codspeed = [
# only run on CI with the latest Python version
'pytest-codspeed==3.2.0; python_version == "3.14" and implementation_name == "cpython"',
'pytest-codspeed==4.1.1; python_version == "3.14" and implementation_name == "cpython"',
]

all = [
Expand Down
4 changes: 2 additions & 2 deletions pytests/test_decode_car.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_decode_car_invalid_header_type() -> None:
header_obj = libipld.encode_dag_cbor('strInsteadOfObj')
libipld.decode_car(header_len + header_obj)

assert "cannot be converted to 'PyDict'" in str(exc_info.value)
assert "cannot be cast as 'dict'" in str(exc_info.value)


def test_decode_car_invalid_header_version_key() -> None:
Expand Down Expand Up @@ -77,7 +77,7 @@ def test_decode_car_invalid_header_roots_value_type() -> None:
header_obj = libipld.encode_dag_cbor({'version': 1, 'roots': 123})
libipld.decode_car(header_len + header_obj)

assert "cannot be converted to 'PyList'" in str(exc_info.value)
assert "cannot be cast as 'list'" in str(exc_info.value)


def test_decode_car_invalid_header_roots_value_empty_list() -> None:
Expand Down
38 changes: 19 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ::libipld::cid::{Cid, Error as CidError, Result as CidResult, Version};
use anyhow::{anyhow, Result};
use byteorder::{BigEndian, ByteOrder};
use multihash::Multihash;
use pyo3::{ffi, prelude::*, types::*, BoundObject, PyObject, Python};
use pyo3::{ffi, prelude::*, types::*, BoundObject, Python};
use pyo3::pybacked::PyBackedStr;

fn cid_hash_to_pydict<'py>(py: Python<'py>, cid: &Cid) -> Bound<'py, PyDict> {
Expand Down Expand Up @@ -56,7 +56,7 @@ fn sort_map_keys(keys: &Bound<PyList>, len: usize) -> Result<Vec<(PyBackedStr, u
let mut keys_str = Vec::with_capacity(len);
for i in 0..len {
let item = keys.get_item(i)?;
let key = match item.downcast::<PyString>() {
let key = match item.cast::<PyString>() {
Ok(k) => k.to_owned(),
Err(_) => return Err(anyhow!("Map keys must be strings")),
};
Expand Down Expand Up @@ -88,11 +88,11 @@ fn sort_map_keys(keys: &Bound<PyList>, len: usize) -> Result<Vec<(PyBackedStr, u
}

fn get_bytes_from_py_any<'py>(obj: &'py Bound<'py, PyAny>) -> PyResult<&'py [u8]> {
if let Ok(b) = obj.downcast::<PyBytes>() {
if let Ok(b) = obj.cast::<PyBytes>() {
Ok(b.as_bytes())
} else if let Ok(ba) = obj.downcast::<PyByteArray>() {
} else if let Ok(ba) = obj.cast::<PyByteArray>() {
Ok(unsafe { ba.as_bytes() })
} else if let Ok(s) = obj.downcast::<PyString>() {
} else if let Ok(s) = obj.cast::<PyString>() {
Ok(s.to_str()?.as_bytes())
} else {
Err(get_err(
Expand All @@ -108,15 +108,15 @@ fn string_new_bound<'py>(py: Python<'py>, s: &[u8]) -> Result<Bound<'py, PyStrin
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
Ok(Bound::from_owned_ptr(py, ffi::PyUnicode_FromStringAndSize(ptr, len)).downcast_into_unchecked())
Ok(Bound::from_owned_ptr(py, ffi::PyUnicode_FromStringAndSize(ptr, len)).cast_into_unchecked())
}
}

fn decode_dag_cbor_to_pyobject<R: Read + Seek>(
py: Python,
r: &mut R,
depth: usize,
) -> Result<PyObject> {
) -> Result<Py<PyAny>> {
unsafe {
if depth > ffi::Py_GetRecursionLimit() as usize {
PyErr::new::<pyo3::exceptions::PyRecursionError, _>(
Expand Down Expand Up @@ -149,7 +149,7 @@ fn decode_dag_cbor_to_pyobject<R: Read + Seek>(
ffi::PyList_SET_ITEM(ptr, i, decode_dag_cbor_to_pyobject(py, r, depth + 1)?.into_ptr());
}

let list: Bound<'_, PyList> = Bound::from_owned_ptr(py, ptr).downcast_into_unchecked();
let list: Bound<'_, PyList> = Bound::from_owned_ptr(py, ptr).cast_into_unchecked();
list.into_pyobject(py)?.into()
}
}
Expand Down Expand Up @@ -268,7 +268,7 @@ fn encode_dag_cbor_from_pyobject<'py, W: Write>(
}

Ok(())
} else if let Ok(l) = obj.downcast::<PyList>() {
} else if let Ok(l) = obj.cast::<PyList>() {
let len = l.len();

encode::write_u64(w, MajorKind::Array, len as u64)?;
Expand All @@ -278,7 +278,7 @@ fn encode_dag_cbor_from_pyobject<'py, W: Write>(
}

Ok(())
} else if let Ok(map) = obj.downcast::<PyDict>() {
} else if let Ok(map) = obj.cast::<PyDict>() {
let len = map.len();
let keys = sort_map_keys(&map.keys(), len)?;
let values = map.values();
Expand All @@ -294,7 +294,7 @@ fn encode_dag_cbor_from_pyobject<'py, W: Write>(
}

Ok(())
} else if let Ok(f) = obj.downcast::<PyFloat>() {
} else if let Ok(f) = obj.cast::<PyFloat>() {
let v = f.value();
if !v.is_finite() {
return Err(NumberOutOfRange::new::<f64>().into());
Expand All @@ -305,7 +305,7 @@ fn encode_dag_cbor_from_pyobject<'py, W: Write>(
w.write_all(&buf)?;

Ok(())
} else if let Ok(b) = obj.downcast::<PyBytes>() {
} else if let Ok(b) = obj.cast::<PyBytes>() {
// FIXME (MarshalX): it's not efficient to try to parse it as CID
let cid = Cid::try_from(b.as_bytes());
if let Ok(_) = cid {
Expand All @@ -324,7 +324,7 @@ fn encode_dag_cbor_from_pyobject<'py, W: Write>(
}

Ok(())
} else if let Ok(s) = obj.downcast::<PyString>() {
} else if let Ok(s) = obj.cast::<PyString>() {
let buf = s.to_str()?.as_bytes();

encode::write_u64(w, MajorKind::TextString, buf.len() as u64)?;
Expand Down Expand Up @@ -402,7 +402,7 @@ fn read_cid_from_bytes<R: Read>(r: &mut R) -> CidResult<Cid> {
}

#[pyfunction]
pub fn decode_car<'py>(py: Python<'py>, data: &[u8]) -> PyResult<(PyObject, Bound<'py, PyDict>)> {
pub fn decode_car<'py>(py: Python<'py>, data: &[u8]) -> PyResult<(Py<PyAny>, Bound<'py, PyDict>)> {
let buf = &mut BufReader::new(Cursor::new(data));

if let Err(_) = read_u64_leb128(buf) {
Expand All @@ -418,15 +418,15 @@ pub fn decode_car<'py>(py: Python<'py>, data: &[u8]) -> PyResult<(PyObject, Boun
));
};

let header = header_obj.downcast_bound::<PyDict>(py)?;
let header = header_obj.cast_bound::<PyDict>(py)?;

let Some(version) = header.get_item("version")? else {
return Err(get_err(
"Failed to read CAR header",
"Version is None".to_string(),
));
};
if version.downcast::<PyInt>()?.extract::<u64>()? != 1 {
if version.cast::<PyInt>()?.extract::<u64>()? != 1 {
return Err(get_err(
"Failed to read CAR header",
"Unsupported version. Version must be 1".to_string(),
Expand All @@ -439,7 +439,7 @@ pub fn decode_car<'py>(py: Python<'py>, data: &[u8]) -> PyResult<(PyObject, Boun
"Roots is None".to_string(),
));
};
if roots.downcast::<PyList>()?.len() == 0 {
if roots.cast::<PyList>()?.len() == 0 {
return Err(get_err(
"Failed to read CAR header",
"Roots is empty. Must be at least one".to_string(),
Expand Down Expand Up @@ -488,7 +488,7 @@ pub fn decode_car<'py>(py: Python<'py>, data: &[u8]) -> PyResult<(PyObject, Boun
}

#[pyfunction]
pub fn decode_dag_cbor(py: Python, data: &[u8]) -> PyResult<PyObject> {
pub fn decode_dag_cbor(py: Python, data: &[u8]) -> PyResult<Py<PyAny>> {
let mut reader = BufReader::new(Cursor::new(data));
let py_object = decode_dag_cbor_to_pyobject(py, &mut reader, 0);
if let Ok(py_object) = py_object {
Expand Down Expand Up @@ -537,7 +537,7 @@ pub fn encode_dag_cbor<'py>(

fn get_cid_from_py_any<'py>(data: &Bound<PyAny>) -> PyResult<Cid> {
let cid: CidResult<Cid>;
if let Ok(s) = data.downcast::<PyString>() {
if let Ok(s) = data.cast::<PyString>() {
cid = Cid::try_from(s.to_str()?);
} else {
cid = Cid::try_from(get_bytes_from_py_any(data)?);
Expand Down
28 changes: 14 additions & 14 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading