Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5ac2808

Browse filesBrowse files
authored
feat: add context manager to mrd (#1724)
feat: add context manager to mrd
1 parent dbd162b commit 5ac2808
Copy full SHA for 5ac2808

File tree

Expand file treeCollapse file tree

3 files changed

+58
-7
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+58
-7
lines changed
Open diff view settings
Collapse file

‎google/cloud/storage/_experimental/asyncio/async_multi_range_downloader.py‎

Copy file name to clipboardExpand all lines: google/cloud/storage/_experimental/asyncio/async_multi_range_downloader.py
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,16 @@ def __init__(
209209
self._download_ranges_id_to_pending_read_ids = {}
210210
self.persisted_size: Optional[int] = None # updated after opening the stream
211211

212+
async def __aenter__(self):
213+
"""Opens the underlying bidi-gRPC connection to read from the object."""
214+
await self.open()
215+
return self
216+
217+
async def __aexit__(self, exc_type, exc_val, exc_tb):
218+
"""Closes the underlying bidi-gRPC connection."""
219+
if self.is_stream_open:
220+
await self.close()
221+
212222
def _on_open_error(self, exc):
213223
"""Extracts routing token and read handle on redirect error during open."""
214224
routing_token, read_handle = _handle_redirect(exc)
Collapse file

‎tests/system/test_zonal.py‎

Copy file name to clipboardExpand all lines: tests/system/test_zonal.py
+7-7Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,19 +116,19 @@ async def _run():
116116
assert object_metadata.size == object_size
117117
assert int(object_metadata.checksums.crc32c) == object_checksum
118118

119-
mrd = AsyncMultiRangeDownloader(grpc_client, _ZONAL_BUCKET, object_name)
120119
buffer = BytesIO()
121-
await mrd.open()
122-
# (0, 0) means read the whole object
123-
await mrd.download_ranges([(0, 0, buffer)])
124-
await mrd.close()
120+
async with AsyncMultiRangeDownloader(
121+
grpc_client, _ZONAL_BUCKET, object_name
122+
) as mrd:
123+
# (0, 0) means read the whole object
124+
await mrd.download_ranges([(0, 0, buffer)])
125+
assert mrd.persisted_size == object_size
126+
125127
assert buffer.getvalue() == object_data
126-
assert mrd.persisted_size == object_size
127128

128129
# Clean up; use json client (i.e. `storage_client` fixture) to delete.
129130
blobs_to_delete.append(storage_client.bucket(_ZONAL_BUCKET).blob(object_name))
130131
del writer
131-
del mrd
132132
gc.collect()
133133

134134
event_loop.run_until_complete(_run())
Collapse file

‎tests/unit/asyncio/test_async_multi_range_downloader.py‎

Copy file name to clipboardExpand all lines: tests/unit/asyncio/test_async_multi_range_downloader.py
+41Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,44 @@ async def test_download_ranges_raises_on_checksum_mismatch(
401401

402402
assert "Checksum mismatch" in str(exc_info.value)
403403
mock_checksum_class.assert_called_once_with(test_data)
404+
405+
@mock.patch(
406+
"google.cloud.storage._experimental.asyncio.async_multi_range_downloader.AsyncMultiRangeDownloader.open",
407+
new_callable=AsyncMock,
408+
)
409+
@mock.patch(
410+
"google.cloud.storage._experimental.asyncio.async_multi_range_downloader.AsyncMultiRangeDownloader.close",
411+
new_callable=AsyncMock,
412+
)
413+
@mock.patch(
414+
"google.cloud.storage._experimental.asyncio.async_grpc_client.AsyncGrpcClient.grpc_client"
415+
)
416+
@pytest.mark.asyncio
417+
async def test_async_context_manager_calls_open_and_close(
418+
self, mock_grpc_client, mock_close, mock_open
419+
):
420+
# Arrange
421+
mrd = AsyncMultiRangeDownloader(
422+
mock_grpc_client, _TEST_BUCKET_NAME, _TEST_OBJECT_NAME
423+
)
424+
425+
# To simulate the behavior of open and close changing the stream state
426+
async def open_side_effect():
427+
mrd._is_stream_open = True
428+
429+
async def close_side_effect():
430+
mrd._is_stream_open = False
431+
432+
mock_open.side_effect = open_side_effect
433+
mock_close.side_effect = close_side_effect
434+
mrd._is_stream_open = False
435+
436+
# Act
437+
async with mrd as downloader:
438+
# Assert
439+
mock_open.assert_called_once()
440+
assert downloader == mrd
441+
assert mrd.is_stream_open
442+
443+
mock_close.assert_called_once()
444+
assert not mrd.is_stream_open

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.