diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ba51fd05..11ef3d7e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -70,7 +70,6 @@ jobs: fail-fast: ${{ !( startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/tags/') ) }} matrix: python-version: - - '3.10' - '3.11' - '3.12' - '3.13' @@ -99,45 +98,28 @@ jobs: - { os: windows-11-arm, target: aarch64, target-triple: aarch64-pc-windows-msvc, python-architecture: arm64 } - # Python 3.10 has no native ARM64 Windows installer - exclude: - - python-version: '3.10' - conf: { os: windows-11-arm } - include: # Windows x86_64 pypy - - conf: { os: windows-latest, target: x86_64, target-triple: x86_64-pc-windows-msvc } - python-version: pypy3.10 - conf: { os: windows-latest, target: x86_64, target-triple: x86_64-pc-windows-msvc } python-version: pypy3.11 # Linux x86_64 pypy - - conf: { os: ubuntu-latest, target: x86_64, target-triple: x86_64-unknown-linux-gnu, manylinux: auto } - python-version: pypy3.10 - conf: { os: ubuntu-latest, target: x86_64, target-triple: x86_64-unknown-linux-gnu, manylinux: auto } python-version: pypy3.11 # Linux arm pypy - - conf: { os: ubuntu-latest, target: aarch64, target-triple: aarch64-unknown-linux-gnu, manylinux: auto } - python-version: pypy3.10 - conf: { os: ubuntu-latest, target: aarch64, target-triple: aarch64-unknown-linux-gnu, manylinux: auto } python-version: pypy3.11 # OSX x86_64 pypy - - conf: { os: macos-15, target: x86_64, target-triple: x86_64-apple-darwin } - python-version: pypy3.10 - conf: { os: macos-15, target: x86_64, target-triple: x86_64-apple-darwin } python-version: pypy3.11 # OSX universal2 pypy - - conf: { os: macos-15, target: universal2, target-triple: x86_64-apple-darwin } - python-version: pypy3.10 - conf: { os: macos-15, target: universal2, target-triple: x86_64-apple-darwin } python-version: pypy3.11 # OSX arm pypy - - conf: { os: macos-15, target: aarch64, target-triple: aarch64-apple-darwin } - python-version: pypy3.10 - conf: { os: macos-15, target: aarch64, target-triple: aarch64-apple-darwin } python-version: pypy3.11 @@ -179,10 +161,8 @@ jobs: delvewheel repair -v "wheels\$($file.Name)" -w "dist" - name: Install built wheel and Test (Native) - # TODO: I'm not sure but the actual collection of tests on windows using pypy3.10 takes forever and/or fails if: | !startsWith(matrix.conf.manylinux, 'musl') && - !( matrix.python-version == 'pypy3.10' && runner.os == 'Windows' ) && ( matrix.conf.target == 'universal2' || (matrix.conf.target == 'aarch64' && runner.os == 'Windows') || (matrix.conf.target == 'x86_64' && runner.os != 'macOS') ) run: | # Second install guarantees it's going to install from local dir w/ --no-index diff --git a/Cargo.lock b/Cargo.lock index 4a985a9e..17e64cd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,7 +251,7 @@ dependencies = [ [[package]] name = "cramjam-python" -version = "2.12.0-rc1" +version = "2.12.0" dependencies = [ "libcramjam 0.8.0", "pyo3", @@ -339,12 +339,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -489,15 +483,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "miniz_oxide" version = "0.8.5" @@ -509,9 +494,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "parking_lot" @@ -559,37 +544,33 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.25.0" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f239d656363bcee73afef85277f1b281e8ac6212a1d42aa90e55b90ed43c47a4" +checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12" dependencies = [ - "indoc", "libc", - "memoffset", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", - "unindent", ] [[package]] name = "pyo3-build-config" -version = "0.25.0" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755ea671a1c34044fa165247aaf6f419ca39caa6003aee791a0df2713d8f1b6d" +checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e" dependencies = [ - "once_cell", "python3-dll-a", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.25.0" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc95a2e67091e44791d4ea300ff744be5293f394f1bafd9f78c080814d35956e" +checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e" dependencies = [ "libc", "pyo3-build-config", @@ -597,9 +578,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.25.0" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a179641d1b93920829a62f15e87c0ed791b6c8db2271ba0fd7c2686090510214" +checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -609,9 +590,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.25.0" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dff85ebcaab8c441b0e3f7ae40a6963ecea8a9f5e74f647e33fcf5ec9a1e89e" +checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -765,9 +746,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "tempfile" @@ -823,12 +804,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" -[[package]] -name = "unindent" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" - [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 3ab002bb..4263fa24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,11 +97,11 @@ wasm32-compat = ["libcramjam/wasm32-compat"] [dependencies] -pyo3 = { version = "^0.25", default-features = false, features = ["macros"] } +pyo3 = { version = "^0.28", default-features = false, features = ["macros"] } libcramjam = { version = "^0.8", default-features = false } [build-dependencies] -pyo3-build-config = "^0.25" +pyo3-build-config = "^0.28" [profile.release] strip = true diff --git a/pyproject.toml b/pyproject.toml index d348d3d2..4145c406 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "cramjam" version = "2.12.0-rc1" keywords = ["compression", "decompression", "snappy", "zstd", "bz2", "gzip", "lz4", "brotli", "deflate", "blosc2"] -requires-python = ">=3.10" +requires-python = ">=3.11" license = { file = "LICENSE" } [project.urls] diff --git a/src/blosc2.rs b/src/blosc2.rs index 261e4d66..c944b70e 100644 --- a/src/blosc2.rs +++ b/src/blosc2.rs @@ -143,7 +143,7 @@ pub mod blosc2 { pub fn decompress_chunk(py: Python, data: BytesType, output_len: Option) -> PyResult { let bytes = data.as_bytes(); let buf = py - .allow_threads(|| libcramjam::blosc2::decompress_chunk(bytes)) + .detach(|| libcramjam::blosc2::decompress_chunk(bytes)) .map(RustyBuffer::from)?; Ok(buf) } @@ -153,7 +153,7 @@ pub mod blosc2 { pub fn decompress_chunk_into(py: Python, input: BytesType, mut output: BytesType) -> PyResult { let bytes = input.as_bytes(); let out = output.as_bytes_mut()?; - let nbytes = py.allow_threads(|| libcramjam::blosc2::decompress_chunk_into(bytes, out))?; + let nbytes = py.detach(|| libcramjam::blosc2::decompress_chunk_into(bytes, out))?; Ok(nbytes) } @@ -176,7 +176,7 @@ pub mod blosc2 { codec: Option, ) -> PyResult { let bytes = data.as_bytes(); - py.allow_threads(|| { + py.detach(|| { let clevel = clevel.map(Into::into); let filter = filter.map(Into::into); let codec = codec.map(Into::into); @@ -200,7 +200,7 @@ pub mod blosc2 { ) -> PyResult { let bytes = input.as_bytes(); let out = output.as_bytes_mut()?; - py.allow_threads(|| { + py.detach(|| { let clevel = clevel.map(Into::into); let filter = filter.map(Into::into); let codec = codec.map(Into::into); diff --git a/src/io.rs b/src/io.rs index 558ae340..3a510b23 100644 --- a/src/io.rs +++ b/src/io.rs @@ -14,7 +14,6 @@ use pyo3::exceptions::{self, PyBufferError}; use pyo3::ffi; use pyo3::prelude::*; use pyo3::types::PyBytes; -use pyo3::IntoPyObjectExt; use std::path::PathBuf; pub(crate) trait AsBytes { @@ -210,7 +209,7 @@ impl PythonBuffer { pub fn as_slice_mut(&mut self) -> PyResult<&mut [u8]> { #[cfg(any(PyPy, Py_GIL_DISABLED))] { - Python::with_gil(|py| { + Python::attach(|py| { let is_memoryview = unsafe { ffi::PyMemoryView_Check(self.owner.as_ptr()) } == 1; if is_memoryview || self.owner.bind(py).is_instance_of::() { #[cfg(PyPy)] @@ -260,13 +259,14 @@ impl PythonBuffer { impl Drop for PythonBuffer { fn drop(&mut self) { - Python::with_gil(|_| unsafe { ffi::PyBuffer_Release(&mut *self.inner) }) + Python::attach(|_| unsafe { ffi::PyBuffer_Release(&mut *self.inner) }) } } -impl<'py> FromPyObject<'py> for PythonBuffer { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { - Self::try_from(obj) +impl<'a, 'py> FromPyObject<'a, 'py> for PythonBuffer { + type Error = PyErr; + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { + Self::try_from(&*obj) } } @@ -285,7 +285,7 @@ impl<'a, 'py> TryFrom<&'a Bound<'py, PyAny>> for PythonBuffer { inner: std::pin::Pin::from(buf), pos: 0, #[cfg(any(PyPy, Py_GIL_DISABLED))] - owner: Python::with_gil(|py| obj.into_py_any(py).unwrap()), + owner: obj.clone().unbind(), }; // sanity checks if buf.inner.shape.is_null() { @@ -629,7 +629,7 @@ impl RustyBuffer { } fn __contains__(&self, py: Python, x: BytesType) -> bool { let bytes = x.as_bytes(); - py.allow_threads(|| self.inner.get_ref().windows(bytes.len()).any(|w| w == bytes)) + py.detach(|| self.inner.get_ref().windows(bytes.len()).any(|w| w == bytes)) } fn __repr__(&mut self, py: Python) -> PyResult { Ok(format!("cramjam.Buffer", self.len(py)?)) diff --git a/src/lib.rs b/src/lib.rs index 6b5e6058..530f0566 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -222,13 +222,13 @@ macro_rules! generic { BytesType::RustyFile(f) => { let borrowed = f.borrow(); let file = &borrowed.inner; - $py.allow_threads(|| { + $py.detach(|| { $op(file, &mut Cursor::new(&mut output) $(, $args)* ) }) }, _ => { let bytes = $input.as_bytes(); - $py.allow_threads(|| { + $py.detach(|| { $op(bytes, &mut Cursor::new(&mut output) $(, $args)* ) }) } @@ -246,20 +246,20 @@ macro_rules! generic { BytesType::RustyFile(f) => { let mut borrowed = f.borrow_mut(); let mut f_out = &mut borrowed.inner; - $py.allow_threads(|| { + $py.detach(|| { $op(f_in, &mut f_out $(, $args)* ) }) }, BytesType::RustyBuffer(buffer) => { let mut borrowed = buffer.borrow_mut(); let mut buf_out = &mut borrowed.inner; - $py.allow_threads(|| { + $py.detach(|| { $op(f_in, &mut buf_out $(, $args)* ) }) }, _ => { let bytes_out = $output.as_bytes_mut()?; - $py.allow_threads(|| { + $py.detach(|| { $op(f_in, &mut Cursor::new(bytes_out) $(, $args)* ) }) } @@ -271,20 +271,20 @@ macro_rules! generic { BytesType::RustyFile(f) => { let mut borrowed = f.borrow_mut(); let mut f_out = &mut borrowed.inner; - $py.allow_threads(|| { + $py.detach(|| { $op(bytes_in, &mut f_out $(, $args)* ) }) }, BytesType::RustyBuffer(buffer) => { let mut borrowed = buffer.borrow_mut(); let mut buf_out = &mut borrowed.inner; - $py.allow_threads(|| { + $py.detach(|| { $op(bytes_in, &mut buf_out $(, $args)* ) }) }, _ => { let bytes_out = $output.as_bytes_mut()?; - $py.allow_threads(|| { + $py.detach(|| { $op(bytes_in, &mut Cursor::new(bytes_out) $(, $args)*) }) } @@ -331,11 +331,11 @@ macro_rules! make_decompressor { BytesType::RustyFile(f) => { let mut borrowed = f.borrow_mut(); let f_in = &mut borrowed.inner; - py.allow_threads(|| libcramjam::$codec::decompress(f_in, inner).map_err(Into::into)) + py.detach(|| libcramjam::$codec::decompress(f_in, inner).map_err(Into::into)) } _ => { let bytes = input.as_bytes(); - py.allow_threads(|| { + py.detach(|| { libcramjam::$codec::decompress(&mut Cursor::new(bytes), inner).map_err(Into::into) }) } @@ -377,7 +377,7 @@ macro_rules! make_decompressor { } fn __contains__(&self, py: Python, x: BytesType) -> bool { let bytes = x.as_bytes(); - py.allow_threads(|| { + py.detach(|| { self.inner .as_ref() .map(|c| c.get_ref().windows(bytes.len()).any(|w| w == bytes)) diff --git a/src/lz4.rs b/src/lz4.rs index 3ceaa898..eda1a148 100644 --- a/src/lz4.rs +++ b/src/lz4.rs @@ -81,7 +81,7 @@ pub mod lz4 { pub fn decompress_block(py: Python, data: BytesType, output_len: Option) -> PyResult { let bytes = data.as_bytes(); - py.allow_threads(|| { + py.detach(|| { match output_len { Some(n) => { let mut buf = vec![0u8; n]; @@ -123,7 +123,7 @@ pub mod lz4 { store_size: Option, ) -> PyResult { let bytes = data.as_bytes(); - py.allow_threads(|| { + py.detach(|| { libcramjam::lz4::block::compress_vec(bytes, compression.map(|v| v as _), acceleration, store_size) }) .map_err(CompressionError::from_err) @@ -160,7 +160,7 @@ pub mod lz4 { } let out_bytes = output.as_bytes_mut()?; - py.allow_threads( + py.detach( || match libcramjam::lz4::block::decompress_into(bytes, out_bytes, Some(size_stored)) { Ok(r) => Ok(r), // Fallback and try negation of stored size, incase we/they got it wrong; @@ -202,7 +202,7 @@ pub mod lz4 { ) -> PyResult { let bytes = data.as_bytes(); let out_bytes = output.as_bytes_mut()?; - py.allow_threads(|| { + py.detach(|| { libcramjam::lz4::block::compress_into( bytes, out_bytes, diff --git a/src/snappy.rs b/src/snappy.rs index b5512ad2..2fe0b57a 100644 --- a/src/snappy.rs +++ b/src/snappy.rs @@ -54,7 +54,7 @@ pub mod snappy { #[pyo3(signature = (data, output_len=None))] pub fn decompress_raw(py: Python, data: BytesType, output_len: Option) -> PyResult { let bytes = data.as_bytes(); - py.allow_threads(|| libcramjam::snappy::raw::decompress_vec(bytes)) + py.detach(|| libcramjam::snappy::raw::decompress_vec(bytes)) .map_err(DecompressionError::from_err) .map(From::from) } @@ -72,7 +72,7 @@ pub mod snappy { #[pyo3(signature = (data, output_len=None))] pub fn compress_raw(py: Python, data: BytesType, output_len: Option) -> PyResult { let bytes = data.as_bytes(); - py.allow_threads(|| libcramjam::snappy::raw::compress_vec(bytes)) + py.detach(|| libcramjam::snappy::raw::compress_vec(bytes)) .map_err(CompressionError::from_err) .map(From::from) } @@ -94,7 +94,7 @@ pub mod snappy { pub fn compress_raw_into(py: Python, input: BytesType, mut output: BytesType) -> PyResult { let bytes_in = input.as_bytes(); let bytes_out = output.as_bytes_mut()?; - py.allow_threads(|| libcramjam::snappy::raw::compress(bytes_in, bytes_out)) + py.detach(|| libcramjam::snappy::raw::compress(bytes_in, bytes_out)) .map_err(CompressionError::from_err) } @@ -103,7 +103,7 @@ pub mod snappy { pub fn decompress_raw_into(py: Python, input: BytesType, mut output: BytesType) -> PyResult { let bytes_in = input.as_bytes(); let bytes_out = output.as_bytes_mut()?; - py.allow_threads(|| libcramjam::snappy::raw::decompress(bytes_in, bytes_out)) + py.detach(|| libcramjam::snappy::raw::decompress(bytes_in, bytes_out)) .map_err(DecompressionError::from_err) } diff --git a/src/xz.rs b/src/xz.rs index 6ac8a17f..1c480f03 100644 --- a/src/xz.rs +++ b/src/xz.rs @@ -130,7 +130,7 @@ pub mod xz { /// Available Filter IDs #[derive(Clone, Debug, PartialEq)] - #[pyclass(eq, eq_int)] + #[pyclass(eq, eq_int, from_py_object)] #[allow(missing_docs)] pub enum Filter { Arm, @@ -150,7 +150,7 @@ pub mod xz { /// MatchFinder, used with Options.mf attribute #[derive(Clone, Debug, PartialEq)] - #[pyclass(eq, eq_int)] + #[pyclass(eq, eq_int, from_py_object)] #[allow(missing_docs)] pub enum MatchFinder { HashChain3, @@ -174,7 +174,7 @@ pub mod xz { /// MatchFinder, used with Options.mode attribute #[derive(Clone, Debug, PartialEq)] - #[pyclass(eq, eq_int)] + #[pyclass(eq, eq_int, from_py_object)] #[allow(missing_docs)] pub enum Mode { Fast, @@ -192,7 +192,7 @@ pub mod xz { /// FilterChain, similar to the default Python XZ filter chain which is a list of /// dicts. #[derive(Debug, Clone)] - #[pyclass] + #[pyclass(from_py_object)] pub struct FilterChain(Vec); #[pymethods] @@ -229,7 +229,7 @@ pub mod xz { /// FilterChainItem. In Python's lzma module, this represents a single dict in the /// filter chain list. To be added to the `FilterChain` #[derive(Clone, Debug, Default)] - #[pyclass] + #[pyclass(from_py_object)] pub struct FilterChainItem { filter: Filter, options: Options, @@ -250,7 +250,7 @@ pub mod xz { /// #[derive(Clone, Debug, Default)] - #[pyclass] + #[pyclass(from_py_object)] pub struct Options { preset: Option, dict_size: Option, @@ -331,7 +331,7 @@ pub mod xz { /// Possible formats #[derive(Clone, Debug, PartialEq)] - #[pyclass(eq, eq_int)] + #[pyclass(eq, eq_int, from_py_object)] pub enum Format { /// Auto select the format, for compression this is XZ, /// for decompression it will be determined by the compressed input. @@ -362,7 +362,7 @@ pub mod xz { /// Possible Check configurations #[derive(Debug, Clone, PartialEq)] - #[pyclass(eq, eq_int)] + #[pyclass(eq, eq_int, from_py_object)] #[allow(missing_docs)] pub enum Check { Crc64,