| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,3 +33,9 @@ def raise_if_no_fast_crc32c(): | |||
| 33 | 33 | "C extension is required for faster data integrity checks." | |
| 34 | 34 | "For more information, see https://github.com/googleapis/python-crc32c." | |
| 35 | 35 | ) | |
| 36 | + | ||
| 37 | + | ||
| 38 | + def update_write_handle_if_exists(obj, response): | ||
| 39 | + """Update the write_handle attribute of an object if it exists in the response.""" | ||
| 40 | + if hasattr(response, "write_handle") and response.write_handle is not None: | ||
| 41 | + obj.write_handle = response.write_handle | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,7 +27,7 @@ | |||
| 27 | 27 | from google_crc32c import Checksum | |
| 28 | 28 | from google.api_core import exceptions | |
| 29 | 29 | ||
| 30 | - from ._utils import raise_if_no_fast_crc32c | ||
| 30 | + from . import _utils | ||
| 31 | 31 | from google.cloud import _storage_v2 | |
| 32 | 32 | from google.cloud.storage._experimental.asyncio.async_grpc_client import ( | |
| 33 | 33 | AsyncGrpcClient, | |
@@ -121,7 +121,7 @@ def __init__( | |||
| 121 | 121 | servers. Default is `_DEFAULT_FLUSH_INTERVAL_BYTES`. | |
| 122 | 122 | Must be a multiple of `_MAX_CHUNK_SIZE_BYTES`. | |
| 123 | 123 | """ | |
| 124 | - raise_if_no_fast_crc32c() | ||
| 124 | + _utils.raise_if_no_fast_crc32c() | ||
| 125 | 125 | self.client = client | |
| 126 | 126 | self.bucket_name = bucket_name | |
| 127 | 127 | self.object_name = object_name | |
@@ -175,6 +175,7 @@ async def state_lookup(self) -> int: | |||
| 175 | 175 | ) | |
| 176 | 176 | ) | |
| 177 | 177 | response = await self.write_obj_stream.recv() | |
| 178 | + _utils.update_write_handle_if_exists(self, response) | ||
| 178 | 179 | self.persisted_size = response.persisted_size | |
| 179 | 180 | return self.persisted_size | |
| 180 | 181 | ||
@@ -253,6 +254,7 @@ async def append(self, data: bytes) -> None: | |||
| 253 | 254 | ||
| 254 | 255 | if is_last_chunk: | |
| 255 | 256 | response = await self.write_obj_stream.recv() | |
| 257 | + _utils.update_write_handle_if_exists(self, response) | ||
| 256 | 258 | self.persisted_size = response.persisted_size | |
| 257 | 259 | self.offset = self.persisted_size | |
| 258 | 260 | self.bytes_appended_since_last_flush = 0 | |
@@ -295,6 +297,7 @@ async def flush(self) -> int: | |||
| 295 | 297 | ) | |
| 296 | 298 | ) | |
| 297 | 299 | response = await self.write_obj_stream.recv() | |
| 300 | + _utils.update_write_handle_if_exists(self, response) | ||
| 298 | 301 | self.persisted_size = response.persisted_size | |
| 299 | 302 | self.offset = self.persisted_size | |
| 300 | 303 | return self.persisted_size | |
@@ -351,6 +354,7 @@ async def finalize(self) -> _storage_v2.Object: | |||
| 351 | 354 | _storage_v2.BidiWriteObjectRequest(finish_write=True) | |
| 352 | 355 | ) | |
| 353 | 356 | response = await self.write_obj_stream.recv() | |
| 357 | + _utils.update_write_handle_if_exists(self, response) | ||
| 354 | 358 | self.object_resource = response.resource | |
| 355 | 359 | self.persisted_size = self.object_resource.size | |
| 356 | 360 | await self.write_obj_stream.close() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,7 @@ | |||
| 22 | 22 | ||
| 23 | 23 | """ | |
| 24 | 24 | from typing import Optional | |
| 25 | + from . import _utils | ||
| 25 | 26 | from google.cloud import _storage_v2 | |
| 26 | 27 | from google.cloud.storage._experimental.asyncio.async_grpc_client import AsyncGrpcClient | |
| 27 | 28 | from google.cloud.storage._experimental.asyncio.async_abstract_object_stream import ( | |
@@ -190,7 +191,7 @@ async def requests_done(self): | |||
| 190 | 191 | """Signals that all requests have been sent.""" | |
| 191 | 192 | ||
| 192 | 193 | await self.socket_like_rpc.send(None) | |
| 193 | - await self.socket_like_rpc.recv() | ||
| 194 | + _utils.update_write_handle_if_exists(self, await self.socket_like_rpc.recv()) | ||
| 194 | 195 | ||
| 195 | 196 | async def send( | |
| 196 | 197 | self, bidi_write_object_request: _storage_v2.BidiWriteObjectRequest | |
@@ -218,7 +219,9 @@ async def recv(self) -> _storage_v2.BidiWriteObjectResponse: | |||
| 218 | 219 | """ | |
| 219 | 220 | if not self._is_stream_open: | |
| 220 | 221 | raise ValueError("Stream is not open") | |
| 221 | - return await self.socket_like_rpc.recv() | ||
| 222 | + response = await self.socket_like_rpc.recv() | ||
| 223 | + _utils.update_write_handle_if_exists(self, response) | ||
| 224 | + return response | ||
| 222 | 225 | ||
| 223 | 226 | @property | |
| 224 | 227 | def is_stream_open(self) -> bool: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,8 +24,10 @@ | |||
| 24 | 24 | BUCKET = "my-bucket" | |
| 25 | 25 | OBJECT = "my-object" | |
| 26 | 26 | GENERATION = 12345 | |
| 27 | - WRITE_HANDLE = b"test-handle" | ||
| 28 | - WRITE_HANDLE_PROTO = _storage_v2.BidiWriteHandle(handle=WRITE_HANDLE) | ||
| 27 | + WRITE_HANDLE_BYTES = b"test-handle" | ||
| 28 | + NEW_WRITE_HANDLE_BYTES = b"new-test-handle" | ||
| 29 | + WRITE_HANDLE_PROTO = _storage_v2.BidiWriteHandle(handle=WRITE_HANDLE_BYTES) | ||
| 30 | + NEW_WRITE_HANDLE_PROTO = _storage_v2.BidiWriteHandle(handle=NEW_WRITE_HANDLE_BYTES) | ||
| 29 | 31 | ||
| 30 | 32 | ||
| 31 | 33 | @pytest.fixture | |
@@ -151,7 +153,9 @@ async def test_open_for_new_object(mock_async_bidi_rpc, mock_client): | |||
| 151 | 153 | @mock.patch( | |
| 152 | 154 | "google.cloud.storage._experimental.asyncio.async_write_object_stream.AsyncBidiRpc" | |
| 153 | 155 | ) | |
| 154 | - async def test_open_for_new_object_with_generation_zero(mock_async_bidi_rpc, mock_client): | ||
| 156 | + async def test_open_for_new_object_with_generation_zero( | ||
| 157 | + mock_async_bidi_rpc, mock_client | ||
| 158 | + ): | ||
| 155 | 159 | """Test opening a stream for a new object.""" | |
| 156 | 160 | # Arrange | |
| 157 | 161 | socket_like_rpc = mock.AsyncMock() | |
@@ -487,3 +491,95 @@ async def test_requests_done(mock_cls_async_bidi_rpc, mock_client): | |||
| 487 | 491 | # Assert | |
| 488 | 492 | write_obj_stream.socket_like_rpc.send.assert_called_once_with(None) | |
| 489 | 493 | write_obj_stream.socket_like_rpc.recv.assert_called_once() | |
| 494 | + | ||
| 495 | + | ||
| 496 | + @pytest.mark.asyncio | ||
| 497 | + @mock.patch( | ||
| 498 | + "google.cloud.storage._experimental.asyncio.async_write_object_stream.AsyncBidiRpc" | ||
| 499 | + ) | ||
| 500 | + async def test_open_for_existing_object_with_none_size( | ||
| 501 | + mock_async_bidi_rpc, mock_client | ||
| 502 | + ): | ||
| 503 | + """Test opening a stream for an existing object where size is None.""" | ||
| 504 | + # Arrange | ||
| 505 | + socket_like_rpc = mock.AsyncMock() | ||
| 506 | + mock_async_bidi_rpc.return_value = socket_like_rpc | ||
| 507 | + socket_like_rpc.open = mock.AsyncMock() | ||
| 508 | + | ||
| 509 | + mock_response = mock.MagicMock(spec=_storage_v2.BidiWriteObjectResponse) | ||
| 510 | + mock_response.resource = mock.MagicMock(spec=_storage_v2.Object) | ||
| 511 | + mock_response.resource.size = None | ||
| 512 | + mock_response.resource.generation = GENERATION | ||
| 513 | + mock_response.write_handle = WRITE_HANDLE_PROTO | ||
| 514 | + socket_like_rpc.recv = mock.AsyncMock(return_value=mock_response) | ||
| 515 | + | ||
| 516 | + stream = _AsyncWriteObjectStream( | ||
| 517 | + mock_client, BUCKET, OBJECT, generation_number=GENERATION | ||
| 518 | + ) | ||
| 519 | + | ||
| 520 | + # Act | ||
| 521 | + await stream.open() | ||
| 522 | + | ||
| 523 | + # Assert | ||
| 524 | + assert stream.persisted_size == 0 | ||
| 525 | + | ||
| 526 | + | ||
| 527 | + @pytest.mark.asyncio | ||
| 528 | + @mock.patch( | ||
| 529 | + "google.cloud.storage._experimental.asyncio.async_write_object_stream.AsyncBidiRpc" | ||
| 530 | + ) | ||
| 531 | + async def test_recv_updates_write_handle(mock_cls_async_bidi_rpc, mock_client): | ||
| 532 | + """Test that recv updates the write_handle if present in the response.""" | ||
| 533 | + # Arrange | ||
| 534 | + write_obj_stream = await instantiate_write_obj_stream( | ||
| 535 | + mock_client, mock_cls_async_bidi_rpc, open=True | ||
| 536 | + ) | ||
| 537 | + | ||
| 538 | + assert write_obj_stream.write_handle == WRITE_HANDLE_PROTO # Initial handle | ||
| 539 | + | ||
| 540 | + # GCS can periodicallly update write handle in their responses. | ||
| 541 | + bidi_write_object_response = _storage_v2.BidiWriteObjectResponse( | ||
| 542 | + write_handle=NEW_WRITE_HANDLE_PROTO | ||
| 543 | + ) | ||
| 544 | + write_obj_stream.socket_like_rpc.recv = AsyncMock( | ||
| 545 | + return_value=bidi_write_object_response | ||
| 546 | + ) | ||
| 547 | + | ||
| 548 | + # Act | ||
| 549 | + response = await write_obj_stream.recv() | ||
| 550 | + | ||
| 551 | + # Assert | ||
| 552 | + write_obj_stream.socket_like_rpc.recv.assert_called_once() | ||
| 553 | + assert response == bidi_write_object_response | ||
| 554 | + # asserts that new write handle has been updated. | ||
| 555 | + assert write_obj_stream.write_handle == NEW_WRITE_HANDLE_PROTO | ||
| 556 | + | ||
| 557 | + | ||
| 558 | + @pytest.mark.asyncio | ||
| 559 | + @mock.patch( | ||
| 560 | + "google.cloud.storage._experimental.asyncio.async_write_object_stream.AsyncBidiRpc" | ||
| 561 | + ) | ||
| 562 | + async def test_requests_done_updates_write_handle(mock_cls_async_bidi_rpc, mock_client): | ||
| 563 | + """Test that requests_done updates the write_handle if present in the response.""" | ||
| 564 | + # Arrange | ||
| 565 | + write_obj_stream = await instantiate_write_obj_stream( | ||
| 566 | + mock_client, mock_cls_async_bidi_rpc, open=True | ||
| 567 | + ) | ||
| 568 | + assert write_obj_stream.write_handle == WRITE_HANDLE_PROTO # Initial handle | ||
| 569 | + | ||
| 570 | + # new_write_handle = b"new-test-handle" | ||
| 571 | + bidi_write_object_response = _storage_v2.BidiWriteObjectResponse( | ||
| 572 | + write_handle=NEW_WRITE_HANDLE_PROTO | ||
| 573 | + ) | ||
| 574 | + write_obj_stream.socket_like_rpc.send = AsyncMock() | ||
| 575 | + write_obj_stream.socket_like_rpc.recv = AsyncMock( | ||
| 576 | + return_value=bidi_write_object_response | ||
| 577 | + ) | ||
| 578 | + | ||
| 579 | + # Act | ||
| 580 | + await write_obj_stream.requests_done() | ||
| 581 | + | ||
| 582 | + # Assert | ||
| 583 | + write_obj_stream.socket_like_rpc.send.assert_called_once_with(None) | ||
| 584 | + write_obj_stream.socket_like_rpc.recv.assert_called_once() | ||
| 585 | + assert write_obj_stream.write_handle == NEW_WRITE_HANDLE_PROTO | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments