| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d6b8f55 commit 139390c
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,34 @@ | |||
| 1 | + # Copyright 2025 Google LLC | ||
| 2 | + # | ||
| 3 | + # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + # you may not use this file except in compliance with the License. | ||
| 5 | + # You may obtain a copy of the License at | ||
| 6 | + # | ||
| 7 | + # https://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + # | ||
| 9 | + # Unless required by applicable law or agreed to in writing, software | ||
| 10 | + # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + # See the License for the specific language governing permissions and | ||
| 13 | + # limitations under the License. | ||
| 14 | + | ||
| 15 | + import google_crc32c | ||
| 16 | + | ||
| 17 | + from google.api_core import exceptions | ||
| 18 | + | ||
| 19 | + def raise_if_no_fast_crc32c(): | ||
| 20 | + """Check if the C-accelerated version of google-crc32c is available. | ||
| 21 | + | ||
| 22 | + If not, raise an error to prevent silent performance degradation. | ||
| 23 | + | ||
| 24 | + raises google.api_core.exceptions.FailedPrecondition: If the C extension is not available. | ||
| 25 | + returns: True if the C extension is available. | ||
| 26 | + rtype: bool | ||
| 27 | + | ||
| 28 | + """ | ||
| 29 | + if google_crc32c.implementation != "c": | ||
| 30 | + raise exceptions.FailedPrecondition( | ||
| 31 | + "The google-crc32c package is not installed with C support. " | ||
| 32 | + "C extension is required for faster data integrity checks." | ||
| 33 | + "For more information, see https://github.com/googleapis/python-crc32c." | ||
| 34 | + ) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,10 @@ | |||
| 22 | 22 | ||
| 23 | 23 | """ | |
| 24 | 24 | from typing import Optional, Union | |
| 25 | + | ||
| 26 | + from google_crc32c import Checksum | ||
| 27 | + | ||
| 28 | + from ._utils import raise_if_no_fast_crc32c | ||
| 25 | 29 | from google.cloud import _storage_v2 | |
| 26 | 30 | from google.cloud.storage._experimental.asyncio.async_grpc_client import ( | |
| 27 | 31 | AsyncGrpcClient, | |
@@ -100,6 +104,7 @@ def __init__( | |||
| 100 | 104 | :param write_handle: (Optional) An existing handle for writing the object. | |
| 101 | 105 | If provided, opening the bidi-gRPC connection will be faster. | |
| 102 | 106 | """ | |
| 107 | + raise_if_no_fast_crc32c() | ||
| 103 | 108 | self.client = client | |
| 104 | 109 | self.bucket_name = bucket_name | |
| 105 | 110 | self.object_name = object_name | |
@@ -191,11 +196,13 @@ async def append(self, data: bytes) -> None: | |||
| 191 | 196 | bytes_to_flush = 0 | |
| 192 | 197 | while start_idx < total_bytes: | |
| 193 | 198 | end_idx = min(start_idx + _MAX_CHUNK_SIZE_BYTES, total_bytes) | |
| 199 | + data_chunk = data[start_idx:end_idx] | ||
| 194 | 200 | await self.write_obj_stream.send( | |
| 195 | 201 | _storage_v2.BidiWriteObjectRequest( | |
| 196 | 202 | write_offset=self.offset, | |
| 197 | 203 | checksummed_data=_storage_v2.ChecksummedData( | |
| 198 | - content=data[start_idx:end_idx] | ||
| 204 | + content=data_chunk, | ||
| 205 | + crc32c=int.from_bytes(Checksum(data_chunk).digest(), "big"), | ||
| 199 | 206 | ), | |
| 200 | 207 | ) | |
| 201 | 208 | ) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,12 +14,11 @@ | |||
| 14 | 14 | ||
| 15 | 15 | from __future__ import annotations | |
| 16 | 16 | import asyncio | |
| 17 | - import google_crc32c | ||
| 18 | - from google.api_core import exceptions | ||
| 19 | - from google_crc32c import Checksum | ||
| 20 | - | ||
| 21 | 17 | from typing import List, Optional, Tuple | |
| 22 | 18 | ||
| 19 | + from google_crc32c import Checksum | ||
| 20 | + | ||
| 21 | + from ._utils import raise_if_no_fast_crc32c | ||
| 23 | 22 | from google.cloud.storage._experimental.asyncio.async_read_object_stream import ( | |
| 24 | 23 | _AsyncReadObjectStream, | |
| 25 | 24 | ) | |
@@ -160,14 +159,7 @@ def __init__( | |||
| 160 | 159 | :param read_handle: (Optional) An existing read handle. | |
| 161 | 160 | """ | |
| 162 | 161 | ||
| 163 | - # Verify that the fast, C-accelerated version of crc32c is available. | ||
| 164 | - # If not, raise an error to prevent silent performance degradation. | ||
| 165 | - if google_crc32c.implementation != "c": | ||
| 166 | - raise exceptions.NotFound( | ||
| 167 | - "The google-crc32c package is not installed with C support. " | ||
| 168 | - "Bidi reads require the C extension for data integrity checks." | ||
| 169 | - "For more information, see https://github.com/googleapis/python-crc32c." | ||
| 170 | - ) | ||
| 162 | + raise_if_no_fast_crc32c() | ||
| 171 | 163 | ||
| 172 | 164 | self.client = client | |
| 173 | 165 | self.bucket_name = bucket_name | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,6 +15,9 @@ | |||
| 15 | 15 | import pytest | |
| 16 | 16 | from unittest import mock | |
| 17 | 17 | ||
| 18 | + from google_crc32c import Checksum | ||
| 19 | + | ||
| 20 | + from google.api_core import exceptions | ||
| 18 | 21 | from google.cloud.storage._experimental.asyncio.async_appendable_object_writer import ( | |
| 19 | 22 | AsyncAppendableObjectWriter, | |
| 20 | 23 | ) | |
@@ -85,6 +88,23 @@ def test_init_with_optional_args(mock_write_object_stream, mock_client): | |||
| 85 | 88 | ) | |
| 86 | 89 | ||
| 87 | 90 | ||
| 91 | + @mock.patch("google.cloud.storage._experimental.asyncio._utils.google_crc32c") | ||
| 92 | + @mock.patch( | ||
| 93 | + "google.cloud.storage._experimental.asyncio.async_grpc_client.AsyncGrpcClient.grpc_client" | ||
| 94 | + ) | ||
| 95 | + def test_init_raises_if_crc32c_c_extension_is_missing( | ||
| 96 | + mock_grpc_client, mock_google_crc32c | ||
| 97 | + ): | ||
| 98 | + mock_google_crc32c.implementation = "python" | ||
| 99 | + | ||
| 100 | + with pytest.raises(exceptions.FailedPrecondition) as exc_info: | ||
| 101 | + AsyncAppendableObjectWriter(mock_grpc_client, "bucket", "object") | ||
| 102 | + | ||
| 103 | + assert "The google-crc32c package is not installed with C support" in str( | ||
| 104 | + exc_info.value | ||
| 105 | + ) | ||
| 106 | + | ||
| 107 | + | ||
| 88 | 108 | @pytest.mark.asyncio | |
| 89 | 109 | @mock.patch( | |
| 90 | 110 | "google.cloud.storage._experimental.asyncio.async_appendable_object_writer._AsyncWriteObjectStream" | |
@@ -434,10 +454,15 @@ async def test_append_sends_data_in_chunks(mock_write_object_stream, mock_client | |||
| 434 | 454 | # First chunk | |
| 435 | 455 | assert first_call[0][0].write_offset == 100 | |
| 436 | 456 | assert len(first_call[0][0].checksummed_data.content) == _MAX_CHUNK_SIZE_BYTES | |
| 437 | - | ||
| 457 | + assert first_call[0][0].checksummed_data.crc32c == int.from_bytes( | ||
| 458 | + Checksum(data[:_MAX_CHUNK_SIZE_BYTES]).digest(), byteorder="big" | ||
| 459 | + ) | ||
| 438 | 460 | # Second chunk | |
| 439 | 461 | assert second_call[0][0].write_offset == 100 + _MAX_CHUNK_SIZE_BYTES | |
| 440 | 462 | assert len(second_call[0][0].checksummed_data.content) == 1 | |
| 463 | + assert second_call[0][0].checksummed_data.crc32c == int.from_bytes( | ||
| 464 | + Checksum(data[_MAX_CHUNK_SIZE_BYTES:]).digest(), byteorder="big" | ||
| 465 | + ) | ||
| 441 | 466 | ||
| 442 | 467 | assert writer.offset == 100 + len(data) | |
| 443 | 468 | writer.simple_flush.assert_not_awaited() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -349,9 +349,7 @@ async def test_downloading_without_opening_should_throw_error( | |||
| 349 | 349 | assert str(exc.value) == "Underlying bidi-gRPC stream is not open" | |
| 350 | 350 | assert not mrd.is_stream_open | |
| 351 | 351 | ||
| 352 | - @mock.patch( | ||
| 353 | - "google.cloud.storage._experimental.asyncio.async_multi_range_downloader.google_crc32c" | ||
| 354 | - ) | ||
| 352 | + @mock.patch("google.cloud.storage._experimental.asyncio._utils.google_crc32c") | ||
| 355 | 353 | @mock.patch( | |
| 356 | 354 | "google.cloud.storage._experimental.asyncio.async_grpc_client.AsyncGrpcClient.grpc_client" | |
| 357 | 355 | ) | |
@@ -360,7 +358,7 @@ def test_init_raises_if_crc32c_c_extension_is_missing( | |||
| 360 | 358 | ): | |
| 361 | 359 | mock_google_crc32c.implementation = "python" | |
| 362 | 360 | ||
| 363 | - with pytest.raises(exceptions.NotFound) as exc_info: | ||
| 361 | + with pytest.raises(exceptions.FailedPrecondition) as exc_info: | ||
| 364 | 362 | AsyncMultiRangeDownloader(mock_grpc_client, "bucket", "object") | |
| 365 | 363 | ||
| 366 | 364 | assert "The google-crc32c package is not installed with C support" in str( | |
| Back | FazBrowse Home | New Git URL |
0 commit comments