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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
in a dedicated AGENTS section
- add tests for weak reference upgrade/downgrade and Kani proofs for view helpers
- add Kani proofs covering `Bytes::try_unwrap_owner` and `WeakBytes` upgrade semantics
- add examples for quick start and PyBytes usage
- add examples for quick start and PyAnyBytes usage
- add example showing how to wrap Python `bytes` into `Bytes`
- summarize built-in `ByteSource`s and show how to extend them
- added tests verifying `WeakView` upgrade and drop semantics
Expand Down Expand Up @@ -97,6 +97,9 @@
- implemented `bytes::Buf` for `Bytes` and `From<Bytes>` for `bytes::Bytes` for
seamless integration with Tokio and other libraries
- implemented `ExactSizeIterator` and `FusedIterator` for `BytesIterOffsets`
- added test exposing `PyAnyBytes` as a read-only `memoryview`
- renamed `PyBytes` wrapper to `PyAnyBytes` to avoid confusion
- renamed `py_anybytes` module to `pyanybytes` for consistency

## 0.19.3 - 2025-05-30
- implemented `Error` for `ViewError`
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ name = "from_python"
required-features = ["pyo3"]

[[example]]
name = "pybytes"
name = "pyanybytes"
required-features = ["pyo3"]

[[example]]
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Other optional features provide additional integrations:
- `ownedbytes` &ndash; adds compatibility with [`ownedbytes`](https://crates.io/crates/ownedbytes) and implements its `StableDeref` trait.
- `mmap` &ndash; enables memory-mapped file handling via the `memmap2` crate.
- `zerocopy` &ndash; exposes the [`view`](src/view.rs) module for typed zero-copy access and allows using `zerocopy` types as sources.
- `pyo3` &ndash; builds the [`pybytes`](src/pybytes.rs) module to provide Python bindings for `Bytes`.
- `pyo3` &ndash; builds the [`pyanybytes`](src/pyanybytes.rs) module to provide Python bindings for `Bytes`.
- `winnow` &ndash; implements the [`Stream`](https://docs.rs/winnow/) traits for `Bytes` and offers parsers (`view`, `view_elems(count)`) that return typed `View`s.

Enabling the `pyo3` feature requires the Python development headers and libraries
Expand All @@ -145,7 +145,7 @@ needs these libraries installed; otherwise disable the feature during testing.

- [`examples/quick_start.rs`](examples/quick_start.rs) – the quick start shown above
- [`examples/try_unwrap_owner.rs`](examples/try_unwrap_owner.rs) – reclaim the owner when uniquely referenced
- [`examples/pybytes.rs`](examples/pybytes.rs) – demonstrates the `pyo3` feature using `PyBytes`
- [`examples/pyanybytes.rs`](examples/pyanybytes.rs) – demonstrates the `pyo3` feature using `PyAnyBytes`
- [`examples/from_python.rs`](examples/from_python.rs) – wrap a Python `bytes` object into `Bytes`
- [`examples/python_winnow.rs`](examples/python_winnow.rs) – parse Python bytes with winnow
- [`examples/python_winnow_view.rs`](examples/python_winnow_view.rs) – parse structured data from Python bytes using winnow's `view`
Expand Down Expand Up @@ -179,7 +179,7 @@ development iterations.
- [`ByteSource`](src/bytes.rs) &ndash; trait for objects that can provide bytes.
- [`ByteOwner`](src/bytes.rs) &ndash; keeps backing storage alive.
- [`view` module](src/view.rs) &ndash; typed zero-copy access to bytes.
- [`pybytes` module](src/pybytes.rs) &ndash; Python bindings.
- [`pyanybytes` module](src/pyanybytes.rs) &ndash; Python bindings.

## Acknowledgements
This library started as a fork of the minibyte library in facebooks [sapling scm](https://github.com/facebook/sapling).
Expand Down
4 changes: 2 additions & 2 deletions examples/pybytes.rs → examples/pyanybytes.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use anybytes::{Bytes, PyBytes};
use anybytes::{Bytes, PyAnyBytes};
use pyo3::prelude::*;

fn main() -> PyResult<()> {
Python::with_gil(|py| {
let bytes = Bytes::from(vec![1u8, 2, 3, 4]);
let wrapped = Py::new(py, PyBytes::new(bytes))?;
let wrapped = Py::new(py, PyAnyBytes::new(bytes))?;

let builtins = PyModule::import(py, "builtins")?;
let memoryview = builtins.getattr("memoryview")?.call1((wrapped.bind(py),))?;
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub mod view;

#[cfg(feature = "pyo3")]
/// Python bindings for [`Bytes`].
pub mod pybytes;
pub mod pyanybytes;

#[cfg(feature = "winnow")]
/// Integration with the `winnow` parser library.
Expand All @@ -37,7 +37,7 @@ pub use crate::bytes::ByteSource;
pub use crate::bytes::Bytes;
pub use crate::bytes::WeakBytes;
#[cfg(feature = "pyo3")]
pub use crate::pybytes::PyBytes;
pub use crate::pyanybytes::PyAnyBytes;
#[cfg(feature = "zerocopy")]
pub use crate::view::View;

Expand Down
6 changes: 3 additions & 3 deletions src/pybytes.rs → src/pyanybytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ use crate::Bytes;

/// Python wrapper around [`Bytes`].
#[pyclass(name = "Bytes")]
pub struct PyBytes {
pub struct PyAnyBytes {
bytes: Bytes,
}

#[pymethods]
impl PyBytes {
impl PyAnyBytes {
/// Exposes the bytes to Python's buffer protocol.
///
/// # Safety
Expand All @@ -45,7 +45,7 @@ impl PyBytes {
}
}

impl PyBytes {
impl PyAnyBytes {
/// Wrap a [`Bytes`] instance for Python exposure.
pub fn new(bytes: Bytes) -> Self {
Self { bytes }
Expand Down
30 changes: 30 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,3 +560,33 @@ fn test_area_alignment_padding() {
expected.extend_from_slice(&0x0506u16.to_ne_bytes());
assert_eq!(all.as_ref(), expected.as_slice());
}

#[cfg(feature = "pyo3")]
#[test]
fn test_pyanybytes_memoryview() {
use crate::{Bytes, PyAnyBytes};
use pyo3::types::{PyAnyMethods, PyMemoryView};
use pyo3::{Py, Python};

pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let data = b"memoryview";
let bytes = PyAnyBytes::new(Bytes::from(data.to_vec()));
let py_obj = Py::new(py, bytes).expect("PyAnyBytes");
let view = PyMemoryView::from(py_obj.bind(py).as_any()).expect("memoryview");

let mv_bytes: Vec<u8> = view
.call_method0("tobytes")
.expect("tobytes")
.extract()
.expect("extract bytes");
assert_eq!(mv_bytes.as_slice(), data);

let readonly: bool = view
.getattr("readonly")
.expect("readonly attr")
.extract()
.expect("extract bool");
assert!(readonly);
});
}