From 0e361b4f2af5696694890f8a780ab3031e9db796 Mon Sep 17 00:00:00 2001 From: Mike Flowers Date: Wed, 5 Jun 2024 13:51:23 -0700 Subject: [PATCH 1/4] Remove passing exception as args to super in APIError --- gspread/exceptions.py | 2 +- tests/worksheet_test.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/gspread/exceptions.py b/gspread/exceptions.py index 4701ebdf7..97c5f27d6 100644 --- a/gspread/exceptions.py +++ b/gspread/exceptions.py @@ -40,7 +40,7 @@ class APIError(GSpreadException): such as when we attempt to retrieve things that don't exist.""" def __init__(self, response: Response): - super().__init__(self._extract_error(response)) + super().__init__(response) self.response: Response = response self.error: Mapping[str, Any] = response.json()["error"] self.code: int = self.error["code"] diff --git a/tests/worksheet_test.py b/tests/worksheet_test.py index 51568ad37..22396c684 100644 --- a/tests/worksheet_test.py +++ b/tests/worksheet_test.py @@ -1,4 +1,5 @@ import itertools +import pickle import random import re from inspect import signature @@ -1911,4 +1912,8 @@ def test_add_validation(self): {"spreadsheetId": self.spreadsheet.id, "replies": [{}]}, ) - self.assertRaises(APIError, sheet.update, values="X", range_name="A1") + with self.assertRaises(APIError) as ex: + sheet.update(values="X", range_name="A1") + + # Ensure that the exception is able to be pickled + pickle.loads(pickle.dumps(ex.exception)) From 10b4dd69a41b66c544d3bd5a03976a041bbe5ebb Mon Sep 17 00:00:00 2001 From: Mike Flowers Date: Wed, 5 Jun 2024 16:14:44 -0700 Subject: [PATCH 2/4] Add nosec comments --- tests/worksheet_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/worksheet_test.py b/tests/worksheet_test.py index 22396c684..0037ff4b3 100644 --- a/tests/worksheet_test.py +++ b/tests/worksheet_test.py @@ -1,5 +1,5 @@ import itertools -import pickle +import pickle # nosec import random import re from inspect import signature @@ -1916,4 +1916,4 @@ def test_add_validation(self): sheet.update(values="X", range_name="A1") # Ensure that the exception is able to be pickled - pickle.loads(pickle.dumps(ex.exception)) + pickle.loads(pickle.dumps(ex.exception)) # nosec From 0469541af9627b1a83677f04813eb30e293a94b3 Mon Sep 17 00:00:00 2001 From: Mike Flowers Date: Wed, 12 Jun 2024 08:31:09 -0700 Subject: [PATCH 3/4] Remove obsolete function Remove obsolete function and typing annotations --- gspread/exceptions.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/gspread/exceptions.py b/gspread/exceptions.py index 97c5f27d6..ed7ff7362 100644 --- a/gspread/exceptions.py +++ b/gspread/exceptions.py @@ -6,7 +6,7 @@ """ -from typing import Any, Dict, Mapping, Optional, Union +from typing import Any, Mapping from requests import Response @@ -45,15 +45,6 @@ def __init__(self, response: Response): self.error: Mapping[str, Any] = response.json()["error"] self.code: int = self.error["code"] - def _extract_error( - self, response: Response - ) -> Optional[Dict[str, Union[int, str]]]: - try: - errors = response.json() - return dict(errors["error"]) - except (AttributeError, KeyError, ValueError): - return None - def __str__(self) -> str: return "{}: [{}]: {}".format( self.__class__.__name__, self.code, self.error["message"] From 07bfe851fe6dbb79bda503b68d0b1adb9efe0c7e Mon Sep 17 00:00:00 2001 From: Mike Flowers Date: Mon, 17 Jun 2024 11:47:43 -0700 Subject: [PATCH 4/4] Refactor to implement __reduce__ instead --- gspread/exceptions.py | 8 ++++++-- tests/worksheet_test.py | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/gspread/exceptions.py b/gspread/exceptions.py index ed7ff7362..1cccd2b12 100644 --- a/gspread/exceptions.py +++ b/gspread/exceptions.py @@ -40,9 +40,10 @@ class APIError(GSpreadException): such as when we attempt to retrieve things that don't exist.""" def __init__(self, response: Response): - super().__init__(response) + error = dict(response.json()["error"]) + super().__init__(error) self.response: Response = response - self.error: Mapping[str, Any] = response.json()["error"] + self.error: Mapping[str, Any] = error self.code: int = self.error["code"] def __str__(self) -> str: @@ -53,6 +54,9 @@ def __str__(self) -> str: def __repr__(self) -> str: return self.__str__() + def __reduce__(self) -> tuple: + return self.__class__, (self.response,) + class SpreadsheetNotFound(GSpreadException): """Trying to open non-existent or inaccessible spreadsheet.""" diff --git a/tests/worksheet_test.py b/tests/worksheet_test.py index 0037ff4b3..17b1e1c02 100644 --- a/tests/worksheet_test.py +++ b/tests/worksheet_test.py @@ -1915,5 +1915,7 @@ def test_add_validation(self): with self.assertRaises(APIError) as ex: sheet.update(values="X", range_name="A1") - # Ensure that the exception is able to be pickled - pickle.loads(pickle.dumps(ex.exception)) # nosec + # Ensure that the exception is able to be pickled and unpickled + # Further ensure we are able to access the exception's properties after pickling + reloaded_exception = pickle.loads(pickle.dumps(ex.exception)) # nosec + self.assertEqual(reloaded_exception.args[0]["status"], "INVALID_ARGUMENT")