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
1 change: 1 addition & 0 deletions python/bucketbase/ibucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ def get_object_stream(self, name: PurePosixPath | str) -> ObjectStream:
:return: ObjectStream instance for reading the content
:raises FileNotFoundError: If the object is not found
:raises ValueError: If name is invalid
:raises OSError: If reading fails, including when the object changes while the stream is open
"""
raise NotImplementedError()

Expand Down
49 changes: 36 additions & 13 deletions python/bucketbase/minio_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,26 @@ def read(self, size: int = -1) -> bytes:
return b""

request_headers: DictType | None = {"If-Match": f'"{self._etag}"'} if self._etag else None
response = self._minio_client.get_object(
self._bucket_name,
self._object_name,
offset=self._position,
length=length,
request_headers=request_headers,
version_id=self._version_id,
)
try:
data = response.read()
finally:
response.close()
response.release_conn()
response = self._minio_client.get_object(
self._bucket_name,
self._object_name,
offset=self._position,
length=length,
request_headers=request_headers,
version_id=self._version_id,
)
try:
data = response.read()
finally:
try:
response.close()
finally:
response.release_conn()
except minio.error.S3Error as exc:
if exc.code == "PreconditionFailed":
raise OSError(f"Object {self._object_name} changed while the stream was open") from exc
raise

if len(data) != length:
raise IOError(f"Expected {length} bytes from {self._object_name} at offset {self._position}, but received {len(data)}")
Expand Down Expand Up @@ -253,8 +260,24 @@ def _get_object_name(cls, obj: Object) -> str:
return object_name

def get_object(self, name: PurePosixPath | str) -> bytes:
with self.get_object_stream(name) as response:
_name = self._validate_name(name)
try:
return self._read_object(_name)
except minio.error.S3Error as exc:
if exc.code == "NoSuchKey":
raise FileNotFoundError(f"Object {_name} not found in bucket {self._bucket_name} on Minio") from exc
raise

@ai_blackbox()
def _read_object(self, name: str, *, version_id: str | None = None) -> bytes:
response = self._minio_client.get_object(self._bucket_name, name, version_id=version_id)
try:
return response.read()
finally:
try:
response.close()
finally:
response.release_conn()

@ai_blackbox()
def _open_object_stream(self, name: str, *, version_id: str | None = None) -> ObjectStream:
Expand Down
11 changes: 9 additions & 2 deletions python/bucketbase/versioned_minio_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,15 @@ def list_object_versions(self, name: PurePosixPath | str) -> slist[ObjectVersion
return sstream(listing_itr).filter(lambda obj: self._get_object_name(obj) == _name).map(self._to_object_version).to_list()

def get_object_version(self, name: PurePosixPath | str, version_id: str) -> bytes:
with self.get_object_version_stream(name, version_id) as response:
return response.read()
_name = self._validate_name(name)
validate(isinstance(version_id, str), f"version_id must be str, but got {type(version_id)}", exc=ValueError)

try:
return self._read_object(_name, version_id=version_id)
except minio.error.S3Error as exc:
if exc.code in ("MethodNotAllowed", "NoSuchKey", "NoSuchVersion"):
raise FileNotFoundError(f"Object {_name} version {version_id} not found in bucket {self._bucket_name} on Minio") from exc
raise

def get_object_version_stream(self, name: PurePosixPath | str, version_id: str) -> ObjectStream:
_name = self._validate_name(name)
Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "bucketbase"
version = "1.7.0" # do not edit manually. kept in sync with `tool.commitizen` config via automation
version = "1.7.1" # do not edit manually. kept in sync with `tool.commitizen` config via automation
description = "bucketbase"
authors = [{ name = "Andrei Suiu", email = "andrei.suiu@gmail.com" }]
readme = "README.py.md"
Expand Down
10 changes: 7 additions & 3 deletions python/tests/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from tests.base_config import LocalTestConfig as BaseLocalTestConfig

CONFIG: type[BaseLocalTestConfig]
try:
from tests.local_config import LocalTestConfig
from tests.local_config import LocalTestConfig as CustomLocalTestConfig
except ImportError:
from tests.base_config import LocalTestConfig
CONFIG = LocalTestConfig
CONFIG = BaseLocalTestConfig
else:
CONFIG = CustomLocalTestConfig
Loading
Loading