From 55b130d38e3a76cdafcdb2f4deef0813ea56fe0f Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 18:47:11 -0400 Subject: [PATCH 01/11] use loop instead of if/else for cleaner code --- obstore/src/put.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index 68433107..ac0e349f 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -479,10 +479,11 @@ async fn write_multipart( match reader { PutInput::Pull(mut pull_reader) => loop { let mut scratch_buffer = vec![0; chunk_size]; - let read_size = pull_reader.read(&mut scratch_buffer)?; - if read_size == 0 { - break; - } else { + loop { + let read_size = pull_reader.read(&mut scratch_buffer)?; + if read_size == 0 { + break; + } writer.wait_for_capacity(max_concurrency).await?; writer.write(&scratch_buffer[0..read_size]); } From e824e173a2114c0ffb711751c600941fe0b22bbb Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 18:54:04 -0400 Subject: [PATCH 02/11] for an input buffer, don't copy to a scratch buffer during uploading --- obstore/src/put.rs | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index ac0e349f..7caee787 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -11,7 +11,7 @@ use object_store::{ ObjectStore, PutMode, PutMultipartOptions, PutOptions, PutPayload, PutResult, UpdateVersion, WriteMultipart, }; -use pyo3::exceptions::{PyStopAsyncIteration, PyStopIteration, PyValueError}; +use pyo3::exceptions::{PyOverflowError, PyStopAsyncIteration, PyStopIteration, PyValueError}; use pyo3::prelude::*; use pyo3::pybacked::PyBackedStr; use pyo3::types::PyDict; @@ -475,19 +475,38 @@ async fn write_multipart( chunk_size: usize, max_concurrency: usize, ) -> PyObjectStoreResult<()> { - // Match across pull, push, async push match reader { - PutInput::Pull(mut pull_reader) => loop { - let mut scratch_buffer = vec![0; chunk_size]; - loop { - let read_size = pull_reader.read(&mut scratch_buffer)?; - if read_size == 0 { - break; + PutInput::Pull(mut pull_reader) => { + match pull_reader { + // For an in-memory buffer, we don't need to read out into a scratch buffer and + // thus we don't require any memory overhead from the scratch buffer + PullSource::Buffer(cursor) => { + let start = usize::try_from(cursor.position()).map_err(|err| { + PyOverflowError::new_err(format!("Buffer position is too large: {err}")) + })?; + + let buffer = cursor.into_inner(); + let mut offset = start.min(buffer.len()); + while offset < buffer.len() { + let end = (offset + chunk_size).min(buffer.len()); + writer.wait_for_capacity(max_concurrency).await?; + writer.put(buffer.slice(offset..end)); + offset = end; + } + } + _ => { + let mut scratch_buffer = vec![0; chunk_size]; + loop { + let read_size = pull_reader.read(&mut scratch_buffer)?; + if read_size == 0 { + break; + } + writer.wait_for_capacity(max_concurrency).await?; + writer.write(&scratch_buffer[0..read_size]); + } } - writer.wait_for_capacity(max_concurrency).await?; - writer.write(&scratch_buffer[0..read_size]); } - }, + } PutInput::SyncPush(push_reader) => { for buf in push_reader { writer.wait_for_capacity(max_concurrency).await?; From b51724dd225867d682726fb3a830bb9591ffb182 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 18:57:09 -0400 Subject: [PATCH 03/11] cleaner error message --- obstore/src/put.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index 7caee787..1139c093 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -481,8 +481,11 @@ async fn write_multipart( // For an in-memory buffer, we don't need to read out into a scratch buffer and // thus we don't require any memory overhead from the scratch buffer PullSource::Buffer(cursor) => { - let start = usize::try_from(cursor.position()).map_err(|err| { - PyOverflowError::new_err(format!("Buffer position is too large: {err}")) + let start = usize::try_from(cursor.position()).map_err(|_| { + PyOverflowError::new_err(format!( + "Buffer position {} is too large for this platform's usize", + cursor.position() + )) })?; let buffer = cursor.into_inner(); From ce2ef112823afd5c03f24385634b352d63e44fd3 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 18:58:58 -0400 Subject: [PATCH 04/11] added tests --- tests/test_put.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/test_put.py b/tests/test_put.py index 4f60d5e8..154bd2d1 100644 --- a/tests/test_put.py +++ b/tests/test_put.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import itertools from tempfile import TemporaryDirectory @@ -109,3 +111,53 @@ def test_put_sync_iterable_local_store(): store.put(path, iterator) assert store.get(path).bytes() == data + + +CHUNK_SIZE = 1024 + + +@pytest.mark.parametrize("nbytes", [0, 1, CHUNK_SIZE - 1, CHUNK_SIZE, CHUNK_SIZE + 1]) +def test_put_multipart_buffer_chunk_boundaries(nbytes: int): + """Buffers are chunked correctly regardless of alignment to chunk_size.""" + store = MemoryStore() + data = bytes(range(256)) * (nbytes // 256 + 1) + data = data[:nbytes] + + store.put("file1.txt", data, use_multipart=True, chunk_size=CHUNK_SIZE) + + assert store.get("file1.txt").bytes() == data + + +@pytest.mark.parametrize("wrapper", [bytes, bytearray, memoryview]) +def test_put_multipart_buffer_types( + wrapper: type[bytes | bytearray | memoryview], +): + """Any buffer-protocol input is uploaded verbatim.""" + store = MemoryStore() + data = b"the quick brown fox jumps over the lazy dog," * 500 + + store.put("file1.txt", wrapper(data), use_multipart=True, chunk_size=CHUNK_SIZE) + + assert store.get("file1.txt").bytes() == data + + +def test_put_multipart_buffer_slice(): + """A memoryview into the middle of a larger buffer uploads only its own bytes.""" + store = MemoryStore() + backing = bytes(range(256)) * 40 + view = memoryview(backing)[1000:8000] + + store.put("file1.txt", view, use_multipart=True, chunk_size=CHUNK_SIZE) + + assert store.get("file1.txt").bytes() == backing[1000:8000] + + +def test_put_multipart_buffer_local_store(): + """The buffer multipart path round-trips through a store with real parts.""" + with TemporaryDirectory() as tmpdir: + store = LocalStore(tmpdir) + data = b"the quick brown fox jumps over the lazy dog," * 500 + + store.put("big-data.txt", data, use_multipart=True, chunk_size=CHUNK_SIZE) + + assert store.get("big-data.txt").bytes() == data From e6a97d085d7d387c9233d01f1d0d710835c099f0 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 19:06:19 -0400 Subject: [PATCH 05/11] Make Buffer a first-class PutInput variant --- obstore/src/put.rs | 78 ++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index 1139c093..3322865d 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -65,7 +65,6 @@ impl<'py> FromPyObject<'_, 'py> for PyUpdateVersion { pub(crate) enum PullSource { File(BufReader), FileLike(PyFileLikeObject), - Buffer(Cursor), } impl PullSource { @@ -88,7 +87,6 @@ impl Read for PullSource { match self { Self::File(f) => f.read(buf), Self::FileLike(f) => f.read(buf), - Self::Buffer(f) => f.read(buf), } } } @@ -98,7 +96,6 @@ impl Seek for PullSource { match self { Self::File(f) => f.seek(pos), Self::FileLike(f) => f.seek(pos), - Self::Buffer(f) => f.seek(pos), } } } @@ -199,6 +196,9 @@ impl AsyncPushSource { // #[derive(Debug)] pub(crate) enum PutInput { + /// A buffer protocol object + Buffer(Cursor), + /// Input that we can pull from Pull(PullSource), @@ -222,14 +222,12 @@ impl PutInput { async fn read_all(&mut self) -> PyObjectStoreResult { match self { - Self::Pull(pull_source) => match pull_source { - PullSource::Buffer(buffer) => Ok(buffer.get_ref().clone().into()), - source => { - let mut buf = Vec::new(); - source.read_to_end(&mut buf)?; - Ok(Bytes::from(buf).into()) - } - }, + Self::Buffer(buffer) => Ok(buffer.get_ref().clone().into()), + Self::Pull(pull_source) => { + let mut buf = Vec::new(); + pull_source.read_to_end(&mut buf)?; + Ok(Bytes::from(buf).into()) + } Self::SyncPush(push_source) => push_source.read_all(), Self::AsyncPush(push_source) => push_source.read_all().await, } @@ -246,9 +244,7 @@ impl<'py> FromPyObject<'_, 'py> for PutInput { path, )?)))) } else if let Ok(buffer) = obj.extract::() { - Ok(Self::Pull(PullSource::Buffer(Cursor::new( - buffer.into_inner(), - )))) + Ok(Self::Buffer(Cursor::new(buffer.into_inner()))) } // Check for file-like object else if obj.hasattr(intern!(py, "read"))? && obj.hasattr(intern!(py, "seek"))? { @@ -476,38 +472,32 @@ async fn write_multipart( max_concurrency: usize, ) -> PyObjectStoreResult<()> { match reader { + PutInput::Buffer(cursor) => { + let start = usize::try_from(cursor.position()).map_err(|_| { + PyOverflowError::new_err(format!( + "Buffer position {} is too large for this platform's usize", + cursor.position() + )) + })?; + + let buffer = cursor.into_inner(); + let mut offset = start.min(buffer.len()); + while offset < buffer.len() { + let end = (offset + chunk_size).min(buffer.len()); + writer.wait_for_capacity(max_concurrency).await?; + writer.put(buffer.slice(offset..end)); + offset = end; + } + } PutInput::Pull(mut pull_reader) => { - match pull_reader { - // For an in-memory buffer, we don't need to read out into a scratch buffer and - // thus we don't require any memory overhead from the scratch buffer - PullSource::Buffer(cursor) => { - let start = usize::try_from(cursor.position()).map_err(|_| { - PyOverflowError::new_err(format!( - "Buffer position {} is too large for this platform's usize", - cursor.position() - )) - })?; - - let buffer = cursor.into_inner(); - let mut offset = start.min(buffer.len()); - while offset < buffer.len() { - let end = (offset + chunk_size).min(buffer.len()); - writer.wait_for_capacity(max_concurrency).await?; - writer.put(buffer.slice(offset..end)); - offset = end; - } - } - _ => { - let mut scratch_buffer = vec![0; chunk_size]; - loop { - let read_size = pull_reader.read(&mut scratch_buffer)?; - if read_size == 0 { - break; - } - writer.wait_for_capacity(max_concurrency).await?; - writer.write(&scratch_buffer[0..read_size]); - } + let mut scratch_buffer = vec![0; chunk_size]; + loop { + let read_size = pull_reader.read(&mut scratch_buffer)?; + if read_size == 0 { + break; } + writer.wait_for_capacity(max_concurrency).await?; + writer.write(&scratch_buffer[0..read_size]); } } PutInput::SyncPush(push_reader) => { From a61606c055abdf93c5692820fa54d45369cea464 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 19:08:07 -0400 Subject: [PATCH 06/11] add variant to use_multipart --- obstore/src/put.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index 3322865d..881d878c 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -213,10 +213,11 @@ impl PutInput { /// Whether to use multipart uploads. fn use_multipart(&mut self, chunk_size: usize) -> PyObjectStoreResult { match self { + Self::Buffer(cursor) => Ok(cursor.get_ref().len() > chunk_size), Self::Pull(pull_source) => pull_source.use_multipart(chunk_size), // We always use multipart uploads for push-based sources because we have no way of // knowing how large they'll be and we don't want to buffer them into memory. - _ => Ok(true), + Self::SyncPush(_) | Self::AsyncPush(_) => Ok(true), } } From cbfa9b701ae803cc2fd0674900a76281b37c9c4c Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 19:11:52 -0400 Subject: [PATCH 07/11] don't use cursor abstraction around buffer --- obstore/src/put.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index 881d878c..5fa53a86 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::fs::File; -use std::io::{BufReader, Cursor, Read, Seek, SeekFrom}; +use std::io::{BufReader, Read, Seek, SeekFrom}; use std::path::PathBuf; use std::sync::Arc; @@ -11,7 +11,7 @@ use object_store::{ ObjectStore, PutMode, PutMultipartOptions, PutOptions, PutPayload, PutResult, UpdateVersion, WriteMultipart, }; -use pyo3::exceptions::{PyOverflowError, PyStopAsyncIteration, PyStopIteration, PyValueError}; +use pyo3::exceptions::{PyStopAsyncIteration, PyStopIteration, PyValueError}; use pyo3::prelude::*; use pyo3::pybacked::PyBackedStr; use pyo3::types::PyDict; @@ -197,7 +197,7 @@ impl AsyncPushSource { // #[derive(Debug)] pub(crate) enum PutInput { /// A buffer protocol object - Buffer(Cursor), + Buffer(Bytes), /// Input that we can pull from Pull(PullSource), @@ -213,7 +213,7 @@ impl PutInput { /// Whether to use multipart uploads. fn use_multipart(&mut self, chunk_size: usize) -> PyObjectStoreResult { match self { - Self::Buffer(cursor) => Ok(cursor.get_ref().len() > chunk_size), + Self::Buffer(buffer) => Ok(buffer.len() > chunk_size), Self::Pull(pull_source) => pull_source.use_multipart(chunk_size), // We always use multipart uploads for push-based sources because we have no way of // knowing how large they'll be and we don't want to buffer them into memory. @@ -223,7 +223,7 @@ impl PutInput { async fn read_all(&mut self) -> PyObjectStoreResult { match self { - Self::Buffer(buffer) => Ok(buffer.get_ref().clone().into()), + Self::Buffer(buffer) => Ok(buffer.clone().into()), Self::Pull(pull_source) => { let mut buf = Vec::new(); pull_source.read_to_end(&mut buf)?; @@ -245,7 +245,7 @@ impl<'py> FromPyObject<'_, 'py> for PutInput { path, )?)))) } else if let Ok(buffer) = obj.extract::() { - Ok(Self::Buffer(Cursor::new(buffer.into_inner()))) + Ok(Self::Buffer(buffer.into_inner())) } // Check for file-like object else if obj.hasattr(intern!(py, "read"))? && obj.hasattr(intern!(py, "seek"))? { @@ -473,16 +473,8 @@ async fn write_multipart( max_concurrency: usize, ) -> PyObjectStoreResult<()> { match reader { - PutInput::Buffer(cursor) => { - let start = usize::try_from(cursor.position()).map_err(|_| { - PyOverflowError::new_err(format!( - "Buffer position {} is too large for this platform's usize", - cursor.position() - )) - })?; - - let buffer = cursor.into_inner(); - let mut offset = start.min(buffer.len()); + PutInput::Buffer(buffer) => { + let mut offset = 0; while offset < buffer.len() { let end = (offset + chunk_size).min(buffer.len()); writer.wait_for_capacity(max_concurrency).await?; From 78f60062a5c6021dd61467b15ace840ef4d66173 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 19:22:16 -0400 Subject: [PATCH 08/11] Make chunk_size a NonZeroUsize param --- obstore/src/put.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index 5fa53a86..01a7a19d 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::fs::File; use std::io::{BufReader, Read, Seek, SeekFrom}; +use std::num::NonZeroUsize; use std::path::PathBuf; use std::sync::Arc; @@ -77,8 +78,8 @@ impl PullSource { } /// Whether to use multipart uploads. - fn use_multipart(&mut self, chunk_size: usize) -> PyObjectStoreResult { - Ok(self.nbytes()? > chunk_size) + fn use_multipart(&mut self, chunk_size: NonZeroUsize) -> PyObjectStoreResult { + Ok(self.nbytes()? > chunk_size.get()) } } @@ -211,9 +212,9 @@ pub(crate) enum PutInput { impl PutInput { /// Whether to use multipart uploads. - fn use_multipart(&mut self, chunk_size: usize) -> PyObjectStoreResult { + fn use_multipart(&mut self, chunk_size: NonZeroUsize) -> PyObjectStoreResult { match self { - Self::Buffer(buffer) => Ok(buffer.len() > chunk_size), + Self::Buffer(buffer) => Ok(buffer.len() > chunk_size.get()), Self::Pull(pull_source) => pull_source.use_multipart(chunk_size), // We always use multipart uploads for push-based sources because we have no way of // knowing how large they'll be and we don't want to buffer them into memory. @@ -298,7 +299,7 @@ impl<'py> IntoPyObject<'py> for PyPutResult { } #[pyfunction] -#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=5242880, max_concurrency=12))] +#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=NonZeroUsize::new(5242880).unwrap(), max_concurrency=12))] #[allow(clippy::too_many_arguments)] pub(crate) fn put( py: Python, @@ -309,7 +310,7 @@ pub(crate) fn put( tags: Option, mode: Option, use_multipart: Option, - chunk_size: usize, + chunk_size: NonZeroUsize, max_concurrency: usize, ) -> PyObjectStoreResult { if matches!(file, PutInput::AsyncPush(_)) { @@ -357,7 +358,7 @@ pub(crate) fn put( } #[pyfunction] -#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=5242880, max_concurrency=12))] +#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=NonZeroUsize::new(5242880).unwrap(), max_concurrency=12))] #[allow(clippy::too_many_arguments)] pub(crate) fn put_async( py: Python, @@ -368,7 +369,7 @@ pub(crate) fn put_async( tags: Option, mode: Option, use_multipart: Option, - chunk_size: usize, + chunk_size: NonZeroUsize, max_concurrency: usize, ) -> PyResult> { let mut use_multipart = if let Some(use_multipart) = use_multipart { @@ -439,7 +440,7 @@ async fn put_multipart_inner( store: Arc, path: &Path, reader: PutInput, - chunk_size: usize, + chunk_size: NonZeroUsize, max_concurrency: usize, attributes: Option, tags: Option, @@ -454,7 +455,7 @@ async fn put_multipart_inner( } let upload = store.put_multipart_opts(path, opts).await?; - let mut writer = WriteMultipart::new_with_chunk_size(upload, chunk_size); + let mut writer = WriteMultipart::new_with_chunk_size(upload, chunk_size.get()); // Make sure to call abort if the multipart upload failed for any reason match write_multipart(&mut writer, reader, chunk_size, max_concurrency).await { @@ -469,21 +470,21 @@ async fn put_multipart_inner( async fn write_multipart( writer: &mut WriteMultipart, reader: PutInput, - chunk_size: usize, + chunk_size: NonZeroUsize, max_concurrency: usize, ) -> PyObjectStoreResult<()> { match reader { PutInput::Buffer(buffer) => { let mut offset = 0; while offset < buffer.len() { - let end = (offset + chunk_size).min(buffer.len()); + let end = (offset + chunk_size.get()).min(buffer.len()); writer.wait_for_capacity(max_concurrency).await?; writer.put(buffer.slice(offset..end)); offset = end; } } PutInput::Pull(mut pull_reader) => { - let mut scratch_buffer = vec![0; chunk_size]; + let mut scratch_buffer = vec![0; chunk_size.get()]; loop { let read_size = pull_reader.read(&mut scratch_buffer)?; if read_size == 0 { From 5146d43570997dd6895ce289fb79f3a8df029a38 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 19:22:25 -0400 Subject: [PATCH 09/11] fix chunk_size in stubs --- obstore/python/obstore/_put.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/obstore/python/obstore/_put.pyi b/obstore/python/obstore/_put.pyi index 30ea7f91..79da693e 100644 --- a/obstore/python/obstore/_put.pyi +++ b/obstore/python/obstore/_put.pyi @@ -109,7 +109,7 @@ def put( tags: dict[str, str] | None = None, mode: PutMode | None = None, use_multipart: bool | None = None, - chunk_size: int = ..., + chunk_size: int = 5242880, max_concurrency: int = 12, ) -> PutResult: """Save the provided bytes to the specified location. @@ -179,7 +179,7 @@ async def put_async( tags: dict[str, str] | None = None, mode: PutMode | None = None, use_multipart: bool | None = None, - chunk_size: int = ..., + chunk_size: int = 5242880, max_concurrency: int = 12, ) -> PutResult: """Call `put` asynchronously. From 26d7edf53e764558b8091395d241a8d1225a6bc3 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 19:23:49 -0400 Subject: [PATCH 10/11] keep stub in sync with store.py --- obstore/python/obstore/_put.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/obstore/python/obstore/_put.pyi b/obstore/python/obstore/_put.pyi index 79da693e..47005fc1 100644 --- a/obstore/python/obstore/_put.pyi +++ b/obstore/python/obstore/_put.pyi @@ -109,7 +109,7 @@ def put( tags: dict[str, str] | None = None, mode: PutMode | None = None, use_multipart: bool | None = None, - chunk_size: int = 5242880, + chunk_size: int = 5 * 1024 * 1024, max_concurrency: int = 12, ) -> PutResult: """Save the provided bytes to the specified location. @@ -179,7 +179,7 @@ async def put_async( tags: dict[str, str] | None = None, mode: PutMode | None = None, use_multipart: bool | None = None, - chunk_size: int = 5242880, + chunk_size: int = 5 * 1024 * 1024, max_concurrency: int = 12, ) -> PutResult: """Call `put` asynchronously. From 1a9f2316d95d30c7ff338ad971f75eb6a9694c19 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 20 Aug 2026 19:24:34 -0400 Subject: [PATCH 11/11] use same more readable version in rust signature --- obstore/src/put.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/obstore/src/put.rs b/obstore/src/put.rs index 01a7a19d..c46ea608 100644 --- a/obstore/src/put.rs +++ b/obstore/src/put.rs @@ -299,7 +299,7 @@ impl<'py> IntoPyObject<'py> for PyPutResult { } #[pyfunction] -#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=NonZeroUsize::new(5242880).unwrap(), max_concurrency=12))] +#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=NonZeroUsize::new(5 * 1024 * 1024).unwrap(), max_concurrency=12))] #[allow(clippy::too_many_arguments)] pub(crate) fn put( py: Python, @@ -358,7 +358,7 @@ pub(crate) fn put( } #[pyfunction] -#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=NonZeroUsize::new(5242880).unwrap(), max_concurrency=12))] +#[pyo3(signature = (store, path, file, *, attributes=None, tags=None, mode=None, use_multipart=None, chunk_size=NonZeroUsize::new(5 * 1024 * 1024).unwrap(), max_concurrency=12))] #[allow(clippy::too_many_arguments)] pub(crate) fn put_async( py: Python,