| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d01a4ba commit 72c7a27
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -79,6 +79,9 @@ def __init__( | |||
| 79 | 79 | self.socket_like_rpc: Optional[AsyncBidiRpc] = None | |
| 80 | 80 | self._is_stream_open: bool = False | |
| 81 | 81 | self.persisted_size: Optional[int] = None | |
| 82 | + self.is_finalized: bool = False | ||
| 83 | + self.full_obj_server_crc32c: Optional[int] = None | ||
| 84 | + self.object_metadata: Optional[_storage_v2.Object] = None | ||
| 82 | 85 | ||
| 83 | 86 | async def open(self, metadata: Optional[List[Tuple[str, str]]] = None) -> None: | |
| 84 | 87 | """Opens the bidi-gRPC connection to read from the object. | |
@@ -132,6 +135,18 @@ async def open(self, metadata: Optional[List[Tuple[str, str]]] = None) -> None: | |||
| 132 | 135 | self.generation_number = response.metadata.generation | |
| 133 | 136 | # update persisted size | |
| 134 | 137 | self.persisted_size = response.metadata.size | |
| 138 | + self.object_metadata = response.metadata | ||
| 139 | + if ( | ||
| 140 | + hasattr(response.metadata, "finalize_time") | ||
| 141 | + and response.metadata.finalize_time | ||
| 142 | + and response.metadata.finalize_time.second > 0 | ||
| 143 | + ): | ||
| 144 | + self.is_finalized = True | ||
| 145 | + if ( | ||
| 146 | + hasattr(response.metadata, "checksums") | ||
| 147 | + and response.metadata.checksums | ||
| 148 | + ): | ||
| 149 | + self.full_obj_server_crc32c = response.metadata.checksums.crc32c | ||
| 135 | 150 | ||
| 136 | 151 | if response and response.read_handle: | |
| 137 | 152 | self.read_handle = response.read_handle | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,9 +38,11 @@ async def instantiate_read_obj_stream(mock_client, mock_cls_async_bidi_rpc, open | |||
| 38 | 38 | socket_like_rpc.open = AsyncMock() | |
| 39 | 39 | ||
| 40 | 40 | recv_response = mock.MagicMock(spec=_storage_v2.BidiReadObjectResponse) | |
| 41 | - recv_response.metadata = mock.MagicMock(spec=_storage_v2.Object) | ||
| 41 | + recv_response.metadata = mock.MagicMock() | ||
| 42 | 42 | recv_response.metadata.generation = _TEST_GENERATION_NUMBER | |
| 43 | 43 | recv_response.metadata.size = _TEST_OBJECT_SIZE | |
| 44 | + recv_response.metadata.finalize_time.second = 30 | ||
| 45 | + recv_response.metadata.checksums.crc32c = 98765 | ||
| 44 | 46 | recv_response.read_handle = _TEST_READ_HANDLE | |
| 45 | 47 | socket_like_rpc.recv = AsyncMock(return_value=recv_response) | |
| 46 | 48 | ||
@@ -130,6 +132,8 @@ async def test_open(mock_client, mock_cls_async_bidi_rpc): | |||
| 130 | 132 | assert read_obj_stream.generation_number == _TEST_GENERATION_NUMBER | |
| 131 | 133 | assert read_obj_stream.read_handle == _TEST_READ_HANDLE | |
| 132 | 134 | assert read_obj_stream.persisted_size == _TEST_OBJECT_SIZE | |
| 135 | + assert read_obj_stream.is_finalized is True | ||
| 136 | + assert read_obj_stream.full_obj_server_crc32c == 98765 | ||
| 133 | 137 | assert read_obj_stream.is_stream_open | |
| 134 | 138 | ||
| 135 | 139 | ||
@@ -381,3 +385,36 @@ async def test_recv_updates_read_handle_on_refresh( | |||
| 381 | 385 | ||
| 382 | 386 | await stream.recv() | |
| 383 | 387 | assert stream.read_handle == refreshed_handle | |
| 388 | + | ||
| 389 | + | ||
| 390 | + @mock.patch("google.cloud.storage.asyncio.async_read_object_stream.AsyncBidiRpc") | ||
| 391 | + @mock.patch( | ||
| 392 | + "google.cloud.storage.asyncio.async_grpc_client.AsyncGrpcClient.grpc_client" | ||
| 393 | + ) | ||
| 394 | + @pytest.mark.asyncio | ||
| 395 | + async def test_open_unfinalized_object_skips_checksum( | ||
| 396 | + mock_client, mock_cls_async_bidi_rpc | ||
| 397 | + ): | ||
| 398 | + socket_like_rpc = AsyncMock() | ||
| 399 | + mock_cls_async_bidi_rpc.return_value = socket_like_rpc | ||
| 400 | + socket_like_rpc.open = AsyncMock() | ||
| 401 | + | ||
| 402 | + recv_response = mock.MagicMock(spec=_storage_v2.BidiReadObjectResponse) | ||
| 403 | + recv_response.metadata = mock.MagicMock() | ||
| 404 | + recv_response.metadata.generation = _TEST_GENERATION_NUMBER | ||
| 405 | + recv_response.metadata.size = _TEST_OBJECT_SIZE | ||
| 406 | + recv_response.metadata.finalize_time.second = 0 # NOT finalized! | ||
| 407 | + recv_response.metadata.checksums.crc32c = 98765 | ||
| 408 | + recv_response.read_handle = _TEST_READ_HANDLE | ||
| 409 | + socket_like_rpc.recv = AsyncMock(return_value=recv_response) | ||
| 410 | + | ||
| 411 | + read_obj_stream = _AsyncReadObjectStream( | ||
| 412 | + client=mock_client, | ||
| 413 | + bucket_name=_TEST_BUCKET_NAME, | ||
| 414 | + object_name=_TEST_OBJECT_NAME, | ||
| 415 | + ) | ||
| 416 | + | ||
| 417 | + await read_obj_stream.open() | ||
| 418 | + | ||
| 419 | + assert read_obj_stream.is_finalized is False | ||
| 420 | + assert read_obj_stream.full_obj_server_crc32c is None | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,32 @@ | |||
| 1 | + # -*- coding: utf-8 -*- | ||
| 2 | + # Copyright 2026 Google LLC | ||
| 3 | + # | ||
| 4 | + # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + # you may not use this file except in compliance with the License. | ||
| 6 | + # You may obtain a copy of the License at | ||
| 7 | + # | ||
| 8 | + # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + # | ||
| 10 | + # Unless required by applicable law or agreed to in writing, software | ||
| 11 | + # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + # See the License for the specific language governing permissions and | ||
| 14 | + # limitations under the License. | ||
| 15 | + | ||
| 16 | + import asyncio | ||
| 17 | + import pytest | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + @pytest.fixture(autouse=True) | ||
| 21 | + def set_event_loop(): | ||
| 22 | + try: | ||
| 23 | + asyncio.get_running_loop() | ||
| 24 | + yield | ||
| 25 | + except RuntimeError: | ||
| 26 | + loop = asyncio.new_event_loop() | ||
| 27 | + asyncio.set_event_loop(loop) | ||
| 28 | + try: | ||
| 29 | + yield | ||
| 30 | + finally: | ||
| 31 | + loop.close() | ||
| 32 | + asyncio.set_event_loop(None) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments