Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit 519a994

Browse files
samples: add samples for bucket encryption enforcement config
Co-authored-by: nidhiii-27 <224584462+nidhiii-27@users.noreply.github.com>
1 parent 774236c commit 519a994

24 files changed

Lines changed: 172 additions & 608 deletions

.librarian/state.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:8e2c32496077054105bd06c54a59d6a6694287bc053588e24debe6da6920ad91
22
libraries:
33
- id: google-cloud-storage
4-
version: 3.10.1
4+
version: 3.9.0
55
last_generated_commit: 5400ccce473c439885bd6bf2924fd242271bfcab
66
apis:
77
- path: google/storage/v2

CHANGELOG.md

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,6 @@
44

55
[1]: https://pypi.org/project/google-cloud-storage/#history
66

7-
## [3.10.1](https://github.com/googleapis/python-storage/compare/v3.10.0...v3.10.1) (2026-03-23)
8-
9-
10-
### Bug Fixes
11-
12-
* raise ValueError if api_endpoint is unset when using AnonymousCredentials in AsyncGrpcClient. (#1778) ([17828ea316872938a98a6360b10a2495c54bbbcb](https://github.com/googleapis/python-storage/commit/17828ea316872938a98a6360b10a2495c54bbbcb))
13-
14-
## [3.10.0](https://github.com/googleapis/python-storage/compare/v3.9.0...v3.10.0) (2026-03-18)
15-
16-
17-
### Features
18-
19-
* [Bucket Encryption Enforcement] add support for bucket encryption enforcement config (#1742) ([2a6e8b00e4e6ff57460373f8e628fd363be47811](https://github.com/googleapis/python-storage/commit/2a6e8b00e4e6ff57460373f8e628fd363be47811))
20-
21-
### Perf Improvments
22-
23-
* [Rapid Buckets Reads] Use raw proto access for read resumption strategy (#1764) ([14cfd61ce35365a409650981239ef742cdf375fb](https://github.com/googleapis/python-storage/commit/14cfd61ce35365a409650981239ef742cdf375fb))
24-
* [Rapid Buckets Benchmarks] init mp pool & grpc client once, use os.sched_setaffinity (#1751) ([a9eb82c1b9b3c6ae5717d47b76284ed0deb5f769](https://github.com/googleapis/python-storage/commit/a9eb82c1b9b3c6ae5717d47b76284ed0deb5f769))
25-
* [Rapid Buckets Writes] don't flush at every append, results in bad perf (#1746) ([ab62d728ac7d7be3c4fe9a99d72e35ead310805a](https://github.com/googleapis/python-storage/commit/ab62d728ac7d7be3c4fe9a99d72e35ead310805a))
26-
27-
28-
### Bug Fixes
29-
30-
* [Windows] skip downloading blobs whose name contain `":" ` eg: `C:` `D:` etc when application runs in Windows. (#1774) ([558198823ed51918db9c0137715d1e7f5b593975](https://github.com/googleapis/python-storage/commit/558198823ed51918db9c0137715d1e7f5b593975))
31-
* [Path Traversal] Prevent path traversal in `download_many_to_path` (#1768) ([700fec3bf7aa37bd5ea4b163cc3f9e8e6892bd5a](https://github.com/googleapis/python-storage/commit/700fec3bf7aa37bd5ea4b163cc3f9e8e6892bd5a))
32-
* [Rapid Buckets] pass token correctly, '&' instead of ',' (#1756) ([d8dd1e074d2431de9b45e0103181dce749a447a0](https://github.com/googleapis/python-storage/commit/d8dd1e074d2431de9b45e0103181dce749a447a0))
33-
34-
357
## [3.9.0](https://github.com/googleapis/python-storage/compare/v3.8.0...v3.9.0) (2026-02-02)
368

379

google/cloud/_storage_v2/gapic_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16-
__version__ = "3.10.1" # {x-release-please-version}
16+
__version__ = "3.9.0" # {x-release-please-version}

google/cloud/storage/asyncio/async_grpc_client.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ def __init__(
5555
attempt_direct_path=True,
5656
):
5757
if isinstance(credentials, auth_credentials.AnonymousCredentials):
58-
if client_options is None or client_options.api_endpoint is None:
59-
raise ValueError(
60-
"Either client_options or `client_option.api_endpoint` is None. Please provide api_endpoint when `AnonymousCredentials` is used "
61-
)
6258
self._grpc_client = self._create_anonymous_client(
6359
client_options, credentials
6460
)

google/cloud/storage/asyncio/retry/reads_resumption_strategy.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,28 +81,23 @@ def update_state_from_response(
8181
self, response: storage_v2.BidiReadObjectResponse, state: Dict[str, Any]
8282
) -> None:
8383
"""Processes a server response, performs integrity checks, and updates state."""
84-
proto = getattr(response, "_pb", response)
8584

8685
# Capture read_handle if provided.
87-
if proto.HasField("read_handle"):
88-
state["read_handle"] = storage_v2.BidiReadHandle(
89-
handle=proto.read_handle.handle
90-
)
86+
if response.read_handle:
87+
state["read_handle"] = response.read_handle
9188

9289
download_states = state["download_states"]
9390

94-
for object_data_range in proto.object_data_ranges:
91+
for object_data_range in response.object_data_ranges:
9592
# Ignore empty ranges or ranges for IDs not in our state
9693
# (e.g., from a previously cancelled request on the same stream).
97-
if not object_data_range.HasField("read_range"):
94+
if not object_data_range.read_range:
9895
logger.warning(
9996
"Received response with missing read_range field; ignoring."
10097
)
10198
continue
10299

103-
read_range_pb = object_data_range.read_range
104-
read_id = read_range_pb.read_id
105-
100+
read_id = object_data_range.read_range.read_id
106101
if read_id not in download_states:
107102
logger.warning(
108103
f"Received data for unknown or stale read_id {read_id}; ignoring."
@@ -112,8 +107,7 @@ def update_state_from_response(
112107
read_state = download_states[read_id]
113108

114109
# Offset Verification
115-
# We must validate data before updating state or writing to buffer.
116-
chunk_offset = read_range_pb.read_offset
110+
chunk_offset = object_data_range.read_range.read_offset
117111
if chunk_offset != read_state.next_expected_offset:
118112
raise DataCorruption(
119113
response,
@@ -122,11 +116,11 @@ def update_state_from_response(
122116
)
123117

124118
# Checksum Verification
125-
checksummed_data = object_data_range.checksummed_data
126-
data = checksummed_data.content
119+
# We must validate data before updating state or writing to buffer.
120+
data = object_data_range.checksummed_data.content
121+
server_checksum = object_data_range.checksummed_data.crc32c
127122

128-
if checksummed_data.HasField("crc32c"):
129-
server_checksum = checksummed_data.crc32c
123+
if server_checksum is not None:
130124
client_checksum = int.from_bytes(Checksum(data).digest(), "big")
131125
if server_checksum != client_checksum:
132126
raise DataCorruption(

google/cloud/storage/exceptions.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@
3333
DataCorruptionDynamicParent = Exception
3434

3535

36-
class InvalidPathError(Exception):
37-
"""Raised when the provided path string is malformed."""
38-
39-
pass
40-
41-
4236
class InvalidResponse(InvalidResponseDynamicParent):
4337
"""Error class for responses which are not in the correct state.
4438

google/cloud/storage/transfer_manager.py

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
from google.cloud.storage._media.requests.upload import XMLMPUContainer
4141
from google.cloud.storage._media.requests.upload import XMLMPUPart
42-
from google.cloud.storage.exceptions import DataCorruption, InvalidPathError
42+
from google.cloud.storage.exceptions import DataCorruption
4343

4444
TM_DEFAULT_CHUNK_SIZE = 32 * 1024 * 1024
4545
DEFAULT_MAX_WORKERS = 8
@@ -263,8 +263,6 @@ def upload_many(
263263

264264

265265
def _resolve_path(target_dir, blob_path):
266-
if os.name == "nt" and ":" in blob_path:
267-
raise InvalidPathError(f"{blob_path} cannot be downloaded into {target_dir}")
268266
target_dir = Path(target_dir)
269267
blob_path = Path(blob_path)
270268
# blob_path.anchor will be '/' if `blob_path` is full path else it'll empty.
@@ -807,65 +805,43 @@ def download_many_to_path(
807805
808806
:raises: :exc:`concurrent.futures.TimeoutError` if deadline is exceeded.
809807
810-
:rtype: List[None|Exception|UserWarning]
808+
:rtype: list
811809
:returns: A list of results corresponding to, in order, each item in the
812-
input list. If an exception was received or a download was skipped
813-
(e.g., due to existing file or path traversal), it will be the result
814-
for that operation (as an Exception or UserWarning, respectively).
815-
Otherwise, the result will be None for a successful download.
810+
input list. If an exception was received, it will be the result
811+
for that operation. Otherwise, the return value from the successful
812+
download method is used (which will be None).
816813
"""
817-
results = [None] * len(blob_names)
818814
blob_file_pairs = []
819-
indices_to_process = []
820815

821-
for i, blob_name in enumerate(blob_names):
816+
for blob_name in blob_names:
822817
full_blob_name = blob_name_prefix + blob_name
823-
try:
824-
resolved_path = _resolve_path(destination_directory, blob_name)
825-
except InvalidPathError as e:
826-
msg = f"The blob {blob_name} will **NOT** be downloaded. {e}"
827-
warnings.warn(msg)
828-
results[i] = UserWarning(msg)
829-
continue
818+
resolved_path = _resolve_path(destination_directory, blob_name)
830819
if not resolved_path.parent.is_relative_to(
831820
Path(destination_directory).resolve()
832821
):
833-
msg = (
822+
warnings.warn(
834823
f"The blob {blob_name} will **NOT** be downloaded. "
835824
f"The resolved destination_directory - {resolved_path.parent} - is either invalid or "
836825
f"escapes user provided {Path(destination_directory).resolve()} . Please download this file separately using `download_to_filename`"
837826
)
838-
warnings.warn(msg)
839-
results[i] = UserWarning(msg)
840827
continue
841828

842829
resolved_path = str(resolved_path)
843-
if skip_if_exists and os.path.isfile(resolved_path):
844-
msg = f"The blob {blob_name} is skipped because destination file already exists"
845-
results[i] = UserWarning(msg)
846-
continue
847-
848830
if create_directories:
849831
directory, _ = os.path.split(resolved_path)
850832
os.makedirs(directory, exist_ok=True)
851833
blob_file_pairs.append((bucket.blob(full_blob_name), resolved_path))
852-
indices_to_process.append(i)
853834

854-
many_results = download_many(
835+
return download_many(
855836
blob_file_pairs,
856837
download_kwargs=download_kwargs,
857838
deadline=deadline,
858839
raise_exception=raise_exception,
859840
worker_type=worker_type,
860841
max_workers=max_workers,
861-
skip_if_exists=False, # skip_if_exists is handled in the loop above
842+
skip_if_exists=skip_if_exists,
862843
)
863844

864-
for meta_index, result in zip(indices_to_process, many_results):
865-
results[meta_index] = result
866-
867-
return results
868-
869845

870846
def download_chunks_concurrently(
871847
blob,

google/cloud/storage/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
__version__ = "3.10.1"
15+
__version__ = "3.9.0"

noxfile.py

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
nox.options.sessions = [
4545
"blacken",
4646
"conftest_retry",
47-
"conftest_retry_bidi",
4847
"docfx",
4948
"docs",
5049
"lint",
@@ -222,9 +221,10 @@ def system(session):
222221
@nox.session(python=CONFORMANCE_TEST_PYTHON_VERSIONS)
223222
def conftest_retry(session):
224223
"""Run the retry conformance test suite."""
225-
json_conformance_tests = "tests/conformance/test_conformance.py"
224+
conformance_test_folder_path = os.path.join("tests", "conformance")
225+
conformance_test_folder_exists = os.path.exists(conformance_test_folder_path)
226226
# Environment check: only run tests if found.
227-
if not os.path.exists(json_conformance_tests):
227+
if not conformance_test_folder_exists:
228228
session.skip("Conformance tests were not found")
229229

230230
constraints_path = str(
@@ -236,6 +236,10 @@ def conftest_retry(session):
236236
session.install(
237237
"pytest",
238238
"pytest-xdist",
239+
"pytest-asyncio",
240+
"grpcio",
241+
"grpcio-status",
242+
"grpc-google-iam-v1",
239243
"-c",
240244
constraints_path,
241245
)
@@ -247,52 +251,17 @@ def conftest_retry(session):
247251
"pytest",
248252
"-vv",
249253
"-s",
250-
json_conformance_tests,
254+
# "--quiet",
255+
conformance_test_folder_path,
251256
*session.posargs,
252257
]
253258
else:
254-
test_cmd = ["pytest", "-vv", "-s", "-n", "auto", json_conformance_tests]
259+
test_cmd = ["pytest", "-vv", "-s", "-n", "auto", conformance_test_folder_path]
255260

256-
# Run pytest against the conformance tests.
261+
# Run py.test against the conformance tests.
257262
session.run(*test_cmd, env={"DOCKER_API_VERSION": "1.39"})
258263

259264

260-
@nox.session(python=CONFORMANCE_TEST_PYTHON_VERSIONS)
261-
def conftest_retry_bidi(session):
262-
"""Run the retry conformance test suite."""
263-
264-
constraints_path = str(
265-
CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
266-
)
267-
268-
# Install all test dependencies and pytest plugin to run tests in parallel.
269-
# Then install this package in-place.
270-
session.install(
271-
"pytest",
272-
"pytest-xdist",
273-
"pytest-asyncio",
274-
"grpcio",
275-
"grpcio-status",
276-
"grpc-google-iam-v1",
277-
"-c",
278-
constraints_path,
279-
)
280-
session.install("-e", ".", "-c", constraints_path)
281-
282-
bidi_tests = [
283-
"tests/conformance/test_bidi_reads.py",
284-
"tests/conformance/test_bidi_writes.py",
285-
]
286-
for test_file in bidi_tests:
287-
session.run(
288-
"pytest",
289-
"-vv",
290-
"-s",
291-
test_file,
292-
env={"DOCKER_API_VERSION": "1.39"},
293-
)
294-
295-
296265
@nox.session(python=DEFAULT_PYTHON_VERSION)
297266
def cover(session):
298267
"""Run the final coverage report.

samples/generated_samples/snippet_metadata_google.storage.v2.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
],
99
"language": "PYTHON",
1010
"name": "google-cloud-storage",
11-
"version": "3.10.1"
11+
"version": "3.9.0"
1212
},
1313
"snippets": [
1414
{

0 commit comments

Comments
 (0)