From 374a0c3e20ce3e384ad6f571ca5423521897e799 Mon Sep 17 00:00:00 2001 From: Yash Gupta Date: Mon, 5 Oct 2026 13:16:44 +0530 Subject: [PATCH 1/4] docs(firestore): document recursive_delete return value --- .../google-cloud-firestore/google/cloud/firestore_v1/client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py index e29d07cb09ac..6002281218be 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py @@ -330,6 +330,8 @@ def recursive_delete( The BulkWriter used to delete all matching documents. Supply this if you want to override the default throttling behavior. + Returns: + int: The number of documents deleted. """ if bulk_writer is None: bulk_writer = self.bulk_writer() From 84b6d85ebe9b8ce0c2e8c47c958065c45e9abfab Mon Sep 17 00:00:00 2001 From: Yash Gupta Date: Tue, 3 Nov 2026 19:17:02 +0530 Subject: [PATCH 2/4] fix(api-core): make RetryError picklable --- .../google/api_core/exceptions.py | 3 ++ .../tests/unit/test_exceptions.py | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/packages/google-api-core/google/api_core/exceptions.py b/packages/google-api-core/google/api_core/exceptions.py index aa9898c5b9a6..2fadec030b4a 100644 --- a/packages/google-api-core/google/api_core/exceptions.py +++ b/packages/google-api-core/google/api_core/exceptions.py @@ -93,6 +93,9 @@ def cause(self): def __str__(self): return "{}, last exception: {}".format(self.message, self.cause) + def __reduce__(self): + return (type(self), (self.message, self.cause)) + class _GoogleAPICallErrorMeta(type): """Metaclass for registering GoogleAPICallError subclasses.""" diff --git a/packages/google-api-core/tests/unit/test_exceptions.py b/packages/google-api-core/tests/unit/test_exceptions.py index 6a71ddeb9261..a18625377349 100644 --- a/packages/google-api-core/tests/unit/test_exceptions.py +++ b/packages/google-api-core/tests/unit/test_exceptions.py @@ -12,8 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +import copy import http.client import json +import pickle from unittest import mock import pytest @@ -434,3 +436,38 @@ def test_from_grpc_error_misleading_404(): assert exception.grpc_status_code == grpc.StatusCode.UNIMPLEMENTED assert "Received http2 header with status: 404" in exception.message assert "api_endpoint" in exception.message + + +@pytest.mark.parametrize( + "operation", + [ + lambda err: pickle.loads(pickle.dumps(err)), + copy.copy, + copy.deepcopy, + ], + ids=["pickle", "copy", "deepcopy"], +) +@pytest.mark.parametrize( + "cause", + [ + exceptions.ServiceUnavailable("503 backend unavailable"), + None, + ], + ids=["with_cause", "none_cause"], +) +def test_retry_error_copy_and_pickle(operation, cause): + original = exceptions.RetryError("Request timed out", cause) + result = operation(original) + + assert isinstance(result, exceptions.RetryError) + assert result.message == original.message + assert str(result) == str(original) + if cause is None: + assert result.cause is None + assert result.cause == original.cause + else: + if operation is copy.copy: + assert result.cause == original.cause + assert isinstance(result.cause, type(original.cause)) + assert result.cause.message == original.cause.message + assert str(result.cause) == str(original.cause) From 6d7330f340c1a3abf67c853aa4795e5b850aa320 Mon Sep 17 00:00:00 2001 From: Yash Gupta Date: Tue, 3 Nov 2026 19:36:43 +0530 Subject: [PATCH 3/4] fix(api-core): preserve RetryError state when copying --- packages/google-api-core/google/api_core/exceptions.py | 2 +- packages/google-api-core/tests/unit/test_exceptions.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/google-api-core/google/api_core/exceptions.py b/packages/google-api-core/google/api_core/exceptions.py index 2fadec030b4a..120736e2169e 100644 --- a/packages/google-api-core/google/api_core/exceptions.py +++ b/packages/google-api-core/google/api_core/exceptions.py @@ -94,7 +94,7 @@ def __str__(self): return "{}, last exception: {}".format(self.message, self.cause) def __reduce__(self): - return (type(self), (self.message, self.cause)) + return (type(self), (self.message, self.cause), self.__dict__) class _GoogleAPICallErrorMeta(type): diff --git a/packages/google-api-core/tests/unit/test_exceptions.py b/packages/google-api-core/tests/unit/test_exceptions.py index a18625377349..31b592639e33 100644 --- a/packages/google-api-core/tests/unit/test_exceptions.py +++ b/packages/google-api-core/tests/unit/test_exceptions.py @@ -457,17 +457,21 @@ def test_from_grpc_error_misleading_404(): ) def test_retry_error_copy_and_pickle(operation, cause): original = exceptions.RetryError("Request timed out", cause) + original.custom_attr = "test_value" + result = operation(original) assert isinstance(result, exceptions.RetryError) assert result.message == original.message assert str(result) == str(original) + assert result.custom_attr == "test_value" + if cause is None: assert result.cause is None - assert result.cause == original.cause else: if operation is copy.copy: - assert result.cause == original.cause + assert result.cause is original.cause + assert isinstance(result.cause, type(original.cause)) assert result.cause.message == original.cause.message assert str(result.cause) == str(original.cause) From fb4429e40308b20796d8e0d136d16f1cdba398ee Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Tue, 3 Nov 2026 15:08:15 +0000 Subject: [PATCH 4/4] revert unrelated change --- .../google-cloud-firestore/google/cloud/firestore_v1/client.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py index 1ba00217a683..9f35fb3f59d5 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py @@ -330,8 +330,6 @@ def recursive_delete( The BulkWriter used to delete all matching documents. Supply this if you want to override the default throttling behavior. - Returns: - int: The number of documents deleted. """ if bulk_writer is None: bulk_writer = self.bulk_writer()