Skip to content

Commit b4a8f2a

Browse files
committed
refactor(buffered): drop ObjectMeta from ReadableFile
1 parent e3a6e25 commit b4a8f2a

4 files changed

Lines changed: 17 additions & 58 deletions

File tree

obstore/python/obstore/_buffered.pyi

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ from contextlib import AbstractAsyncContextManager, AbstractContextManager
33

44
from ._attributes import Attributes
55
from ._bytes import Bytes
6-
from ._list import ObjectMeta
76
from ._store import ObjectStore
87

98
if sys.version_info >= (3, 11):
@@ -33,9 +32,7 @@ def open_reader(
3332
buffer_size: The minimum number of bytes to read in a single request. Up to `buffer_size` bytes will be buffered in memory.
3433
size: Optional byte size of the object. When provided, skips the HEAD request used to fetch the file size. Useful for callers that already know the size from external metadata.
3534
36-
The caller is responsible for accuracy: a value larger than the actual file surfaces as a read-time range error, a value smaller causes silent truncation.
37-
38-
When `size` is provided, the resulting reader's `meta` attribute omits `last_modified` (since it was not fetched). Callers that need that field should call `open_reader` without `size`. Defaults to `None`.
35+
The caller is responsible for accuracy: a value larger than the actual file surfaces as a read-time range error, a value smaller causes silent truncation. Defaults to `None`.
3936
4037
Returns:
4138
ReadableFile
@@ -94,10 +91,6 @@ class ReadableFile:
9491
This is currently a no-op.
9592
"""
9693

97-
@property
98-
def meta(self) -> ObjectMeta:
99-
"""Access the metadata of the underlying file."""
100-
10194
def read(self, size: int | None = None, /) -> Bytes:
10295
"""Read up to `size` bytes from the object and return them.
10396
@@ -176,10 +169,6 @@ class AsyncReadableFile:
176169
This is currently a no-op.
177170
"""
178171

179-
@property
180-
def meta(self) -> ObjectMeta:
181-
"""Access the metadata of the underlying file."""
182-
183172
async def read(self, size: int | None = None, /) -> Bytes:
184173
"""Read up to `size` bytes from the object and return them.
185174

obstore/src/buffered.rs

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ use std::io::SeekFrom;
22
use std::sync::Arc;
33

44
use bytes::Bytes;
5-
use chrono::{DateTime, Utc};
6-
use indexmap::IndexMap;
75
use object_store::buffered::{BufReader, BufWriter};
86
use object_store::{ObjectMeta, ObjectStore, ObjectStoreExt};
97
use pyo3::exceptions::{PyIOError, PyStopAsyncIteration, PyStopIteration};
108
use pyo3::prelude::*;
11-
use pyo3::types::{PyDict, PyString};
9+
use pyo3::types::PyString;
1210
use pyo3::{intern, IntoPyObjectExt};
1311
use pyo3_async_runtimes::tokio::{future_into_py, get_runtime};
1412
use pyo3_bytes::PyBytes;
@@ -30,10 +28,9 @@ pub(crate) fn open_reader(
3028
) -> PyObjectStoreResult<PyReadableFile> {
3129
let store = store.into_inner();
3230
let runtime = get_runtime();
33-
let was_hinted = size.is_some();
34-
let (reader, meta) =
31+
let (reader, resolved_size) =
3532
py.detach(|| runtime.block_on(create_reader(store, path, buffer_size, size)))?;
36-
Ok(PyReadableFile::new(reader, meta, was_hinted, false))
33+
Ok(PyReadableFile::new(reader, resolved_size, false))
3734
}
3835

3936
#[pyfunction]
@@ -46,10 +43,9 @@ pub(crate) fn open_reader_async(
4643
size: Option<u64>,
4744
) -> PyResult<Bound<PyAny>> {
4845
let store = store.into_inner();
49-
let was_hinted = size.is_some();
5046
future_into_py(py, async move {
51-
let (reader, meta) = create_reader(store, path, buffer_size, size).await?;
52-
Ok(PyReadableFile::new(reader, meta, was_hinted, true))
47+
let (reader, resolved_size) = create_reader(store, path, buffer_size, size).await?;
48+
Ok(PyReadableFile::new(reader, resolved_size, true))
5349
})
5450
}
5551

@@ -58,12 +54,11 @@ async fn create_reader(
5854
path: PyPath,
5955
capacity: usize,
6056
size: Option<u64>,
61-
) -> PyObjectStoreResult<(BufReader, ObjectMeta)> {
57+
) -> PyObjectStoreResult<(BufReader, u64)> {
6258
let meta = match size {
6359
Some(size) => ObjectMeta {
6460
location: path.as_ref().clone(),
65-
last_modified: DateTime::<Utc>::from_timestamp(0, 0)
66-
.expect("unix epoch is a valid DateTime"),
61+
last_modified: Default::default(),
6762
size,
6863
e_tag: None,
6964
version: None,
@@ -73,23 +68,22 @@ async fn create_reader(
7368
.await
7469
.map_err(PyObjectStoreError::ObjectStoreError)?,
7570
};
76-
Ok((BufReader::with_capacity(store, &meta, capacity), meta))
71+
let size = meta.size;
72+
Ok((BufReader::with_capacity(store, &meta, capacity), size))
7773
}
7874

7975
#[pyclass(name = "ReadableFile", frozen)]
8076
pub(crate) struct PyReadableFile {
8177
reader: Arc<Mutex<BufReader>>,
82-
meta: ObjectMeta,
83-
was_hinted: bool,
78+
size: u64,
8479
r#async: bool,
8580
}
8681

8782
impl PyReadableFile {
88-
fn new(reader: BufReader, meta: ObjectMeta, was_hinted: bool, r#async: bool) -> Self {
83+
fn new(reader: BufReader, size: u64, r#async: bool) -> Self {
8984
Self {
9085
reader: Arc::new(Mutex::new(reader)),
91-
meta,
92-
was_hinted,
86+
size,
9387
r#async,
9488
}
9589
}
@@ -110,22 +104,6 @@ impl PyReadableFile {
110104
// `Option<Arc<Mutex<BufReader>>>`.
111105
fn close(&self) {}
112106

113-
#[getter]
114-
fn meta<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
115-
let mut dict = IndexMap::with_capacity(5);
116-
dict.insert("path", self.meta.location.as_ref().into_bound_py_any(py)?);
117-
if !self.was_hinted {
118-
dict.insert(
119-
"last_modified",
120-
self.meta.last_modified.into_bound_py_any(py)?,
121-
);
122-
}
123-
dict.insert("size", self.meta.size.into_bound_py_any(py)?);
124-
dict.insert("e_tag", self.meta.e_tag.clone().into_bound_py_any(py)?);
125-
dict.insert("version", self.meta.version.clone().into_bound_py_any(py)?);
126-
dict.into_pyobject(py)
127-
}
128-
129107
#[pyo3(signature = (size = None, /))]
130108
fn read<'py>(&'py self, py: Python<'py>, size: Option<usize>) -> PyResult<Bound<'py, PyAny>> {
131109
let reader = self.reader.clone();
@@ -203,7 +181,7 @@ impl PyReadableFile {
203181

204182
#[getter]
205183
fn size(&self) -> u64 {
206-
self.meta.size
184+
self.size
207185
}
208186

209187
fn tell<'py>(&'py self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {

tests/test_buffered.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,11 @@ def test_open_reader_size_hint_zero_byte_file():
171171
assert memoryview(b"") == memoryview(file.read())
172172

173173

174-
def test_open_reader_meta_last_modified_depends_on_size_hint():
174+
def test_open_reader_no_longer_exposes_meta():
175175
store = MemoryStore()
176176
data = b"x" * 1000
177177
path = "sized.bin"
178178
obs.put(store, path, data)
179179

180-
hinted = obs.open_reader(store, path, size=len(data))
181-
unhinted = obs.open_reader(store, path)
182-
183-
assert "last_modified" not in hinted.meta
184-
assert "last_modified" in unhinted.meta
185-
assert hinted.meta["size"] == len(data)
186-
assert unhinted.meta["size"] == len(data)
187-
assert hinted.meta["e_tag"] is None
188-
assert hinted.meta["path"] == path
180+
file = obs.open_reader(store, path)
181+
assert not hasattr(file, "meta")

tests/test_fsspec.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ def test_buffered_file_forwards_size_to_open_reader():
167167

168168
assert file.size == 500
169169
assert file._reader.size == 500
170-
assert "last_modified" not in file._reader.meta
171170

172171
data = file.read()
173172
assert len(data) == 500

0 commit comments

Comments
 (0)