From 1ae4d996a99dc79adc53c8e33434e6019d6db645 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Sat, 29 Jan 2022 07:25:01 -0800 Subject: [PATCH 01/14] fix: Rename Error and tighten cache name validations Fix for #23 - CacheValueError is a confusing name for surfacing INVALID_ARGUMENT errors. It can be easily confused with something related to setting a value in Cache. Proposing `CacheValueError` is renamed to `InvalidArgumentError` Fix for #24 - Given the type-safety in python, customers can accidentally pass unsupported types. This results in a ClientSdkException when in fact it is an Invalid Input Error. We already type check our keys, values, ttl, so just adding one for cache name. Though it may be just wise to let the type errors pass through. --- src/momento/_scs_control_client.py | 7 ++--- src/momento/_scs_data_client.py | 31 ++++---------------- src/momento/_utilities/_data_validation.py | 4 +-- src/momento/errors.py | 2 +- src/momento/simple_cache_client.py | 4 +-- tests/test_momento.py | 34 ++++++++++------------ tests/test_momento_async.py | 29 +++++++++--------- 7 files changed, 40 insertions(+), 71 deletions(-) diff --git a/src/momento/_scs_control_client.py b/src/momento/_scs_control_client.py index 51c29ff4..6186a743 100644 --- a/src/momento/_scs_control_client.py +++ b/src/momento/_scs_control_client.py @@ -11,6 +11,8 @@ from . import _momento_logger from . import _scs_grpc_manager +from ._utilities._data_validation import _validate_cache_name + class _ScsControlClient: """Momento Internal.""" @@ -56,8 +58,3 @@ def _getStub(self): def close(self): self._grpc_manager.close() - - -def _validate_cache_name(cache_name): - if (cache_name is None): - raise errors.InvalidInputError('Cache Name cannot be None') diff --git a/src/momento/_scs_data_client.py b/src/momento/_scs_data_client.py index d3210f81..b38c225e 100644 --- a/src/momento/_scs_data_client.py +++ b/src/momento/_scs_data_client.py @@ -6,6 +6,8 @@ from . import _momento_logger from . import _scs_grpc_manager +from ._utilities._data_validation import _as_bytes, _validate_ttl, _make_metadata, _validate_cache_name + class _ScsDataClient: """Internal""" @@ -22,8 +24,8 @@ def set(self, cache_name, key, value, ttl_seconds): item_ttl_seconds = self._default_ttlSeconds if ttl_seconds is None else ttl_seconds _validate_ttl(item_ttl_seconds) set_request = cache_client_types.SetRequest() - set_request.cache_key = _asBytes(key, 'Unsupported type for key: ') - set_request.cache_body = _asBytes(value, + set_request.cache_key = _as_bytes(key, 'Unsupported type for key: ') + set_request.cache_body = _as_bytes(value, 'Unsupported type for value: ') set_request.ttl_milliseconds = item_ttl_seconds * 1000 response = self._getStub().Set(set_request, @@ -40,7 +42,7 @@ def get(self, cache_name, key): try: _momento_logger.debug(f'Issuing a get request with key {key}') get_request = cache_client_types.GetRequest() - get_request.cache_key = _asBytes(key, 'Unsupported type for key: ') + get_request.cache_key = _as_bytes(key, 'Unsupported type for key: ') response = self._getStub().Get(get_request, metadata=_make_metadata(cache_name)) _momento_logger.debug(f'Received a get response for {key}') @@ -54,26 +56,3 @@ def _getStub(self): def close(self): self._grpc_manager.close() - - -def _make_metadata(cache_name): - return (('cache', cache_name), ) - - -def _validate_cache_name(cache_name): - if (cache_name is None): - raise errors.InvalidInputError('Cache Name cannot be None') - - -def _asBytes(data, errorMessage): - if (isinstance(data, str)): - return data.encode('utf-8') - if (isinstance(data, bytes)): - return data - raise errors.InvalidInputError(errorMessage + str(type(data))) - - -def _validate_ttl(ttl_seconds): - if (not isinstance(ttl_seconds, int) or ttl_seconds < 0): - raise errors.InvalidInputError( - 'TTL Seconds must be a non-negative integer') diff --git a/src/momento/_utilities/_data_validation.py b/src/momento/_utilities/_data_validation.py index 61dc1c42..186a6c75 100644 --- a/src/momento/_utilities/_data_validation.py +++ b/src/momento/_utilities/_data_validation.py @@ -8,8 +8,8 @@ def _make_metadata(cache_name) -> Metadata: def _validate_cache_name(cache_name): - if cache_name is None: - raise errors.InvalidInputError('Cache Name cannot be None') + if cache_name is None or not isinstance(cache_name, str): + raise errors.InvalidInputError('Cache name must be a non-None value with `str` type') def _as_bytes(data, error_message): diff --git a/src/momento/errors.py b/src/momento/errors.py index 32bab896..307c004a 100644 --- a/src/momento/errors.py +++ b/src/momento/errors.py @@ -49,7 +49,7 @@ def __init__(self, message): super().__init__(message) -class CacheValueError(CacheServiceError): +class InvalidArgumentError(CacheServiceError): """Error raised when service validation fails for provided values""" def __init__(self, message): super().__init__(message) diff --git a/src/momento/simple_cache_client.py b/src/momento/simple_cache_client.py index 9f85597e..ef933130 100644 --- a/src/momento/simple_cache_client.py +++ b/src/momento/simple_cache_client.py @@ -33,7 +33,7 @@ def create_cache(self, cache_name) -> CreateCacheResponse: Raises: InvalidInputError: If cache name is None. ClientSdkError: For any SDK checks that fail. - CacheValueError: If provided cache_name is empty. + InvalidArgumentError: If provided cache_name is empty. CacheExistsError: If cache with the given name already exists. PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. """ @@ -52,7 +52,7 @@ def delete_cache(self, cache_name) -> DeleteCacheResponse: CacheNotFoundError: If an attempt is made to delete a MomentoCache that doesn't exits. InvalidInputError: If cache name is None. ClientSdkError: For any SDK checks that fail. - CacheValueError: If provided cache name is empty + InvalidArgumentError: If provided cache name is empty PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. """ return self._control_client.delete_cache(cache_name) diff --git a/tests/test_momento.py b/tests/test_momento.py index 1f4c70ad..50b5ee2e 100644 --- a/tests/test_momento.py +++ b/tests/test_momento.py @@ -80,19 +80,18 @@ def test_create_cache_throws_already_exists_when_creating_existing_cache(self): self.client.create_cache(_TEST_CACHE_NAME) def test_create_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.CacheValueError): + with self.assertRaises(errors.InvalidArgumentError): self.client.create_cache("") def test_create_cache_throws_validation_exception_for_null_cache_name(self): with self.assertRaises(errors.InvalidInputError) as cm: self.client.create_cache(None) - self.assertEqual('{}'.format(cm.exception), "Cache Name cannot be None") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_create_cache_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: self.client.create_cache(1) - self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: 1 has type int, but expected one of: bytes, unicode") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_create_cache_throws_permission_exception_for_bad_token(self): with simple_cache_client.init(_BAD_AUTH_TOKEN, @@ -121,14 +120,13 @@ def test_delete_cache_throws_invalid_input_for_null_cache_name(self): self.client.delete_cache(None) def test_delete_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.CacheValueError): + with self.assertRaises(errors.InvalidArgumentError): self.client.delete_cache("") def test_delete_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: self.client.delete_cache(1) - self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: 1 has type int, but expected one of: bytes, unicode") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_delete_cache_throws_permission_exception_for_bad_token(self): with simple_cache_client.init(_BAD_AUTH_TOKEN, @@ -238,11 +236,11 @@ def test_set_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidInputError) as cm: self.client.set(None, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), "Cache Name cannot be None") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_set_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheValueError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set("", "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") @@ -260,10 +258,9 @@ def test_set_negative_ttl_throws_exception(self): self.assertEqual('{}'.format(cm.exception), "TTL Seconds must be a non-negative integer") def test_set_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: self.client.set(1, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: Expected str, not ") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_set_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidInputError) as cm: @@ -292,11 +289,11 @@ def test_get_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidInputError) as cm: self.client.get(None, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache Name cannot be None") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_get_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheValueError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.get("", "foo") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") @@ -305,10 +302,9 @@ def test_get_with_null_key_throws_exception(self): self.client.get(_TEST_CACHE_NAME, None) def test_get_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: self.client.get(1, "foo") - self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: Expected str, not ") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_get_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidInputError) as cm: diff --git a/tests/test_momento_async.py b/tests/test_momento_async.py index 3c4ef241..4f810609 100644 --- a/tests/test_momento_async.py +++ b/tests/test_momento_async.py @@ -81,13 +81,13 @@ async def test_create_cache_throws_already_exists_when_creating_existing_cache(s await self.client.create_cache(_TEST_CACHE_NAME) async def test_create_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.CacheValueError): + with self.assertRaises(errors.InvalidArgumentError): await self.client.create_cache("") async def test_create_cache_throws_validation_exception_for_null_cache_name(self): with self.assertRaises(errors.InvalidInputError) as cm: await self.client.create_cache(None) - self.assertEqual('{}'.format(cm.exception), "Cache Name cannot be None") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type.") async def test_create_cache_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.ClientSdkError) as cm: @@ -121,14 +121,13 @@ async def test_delete_cache_throws_invalid_input_for_null_cache_name(self): await self.client.delete_cache(None) async def test_delete_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.CacheValueError): + with self.assertRaises(errors.InvalidArgumentError): await self.client.delete_cache("") async def test_delete_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: await self.client.delete_cache(1) - self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: 1 has type int, but expected one of: bytes, unicode") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_delete_cache_throws_permission_exception_for_bad_token(self): async with simple_cache_client.init(_BAD_AUTH_TOKEN, _DEFAULT_TTL_SECONDS) as simple_cache: @@ -235,11 +234,11 @@ async def test_set_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidInputError) as cm: await self.client.set(None, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), "Cache Name cannot be None") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None string") async def test_set_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheValueError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set("", "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") @@ -257,10 +256,9 @@ async def test_set_negative_ttl_throws_exception(self): self.assertEqual('{}'.format(cm.exception), "TTL Seconds must be a non-negative integer") async def test_set_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: await self.client.set(1, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: Expected str, not ") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_set_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidInputError) as cm: @@ -288,11 +286,11 @@ async def test_get_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidInputError) as cm: await self.client.get(None, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache Name cannot be None") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type.") async def test_get_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheValueError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.get("", "foo") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") @@ -301,10 +299,9 @@ async def test_get_with_null_key_throws_exception(self): await self.client.get(_TEST_CACHE_NAME, None) async def test_get_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: await self.client.get(1, "foo") - self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: Expected str, not ") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type.") async def test_get_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidInputError) as cm: From 86e2901109576320a0bff0e445ad9528d58e9bf3 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Sat, 29 Jan 2022 07:35:16 -0800 Subject: [PATCH 02/14] fix broken error --- src/momento/_cache_service_errors_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/momento/_cache_service_errors_converter.py b/src/momento/_cache_service_errors_converter.py index f4dafb00..99097cb3 100644 --- a/src/momento/_cache_service_errors_converter.py +++ b/src/momento/_cache_service_errors_converter.py @@ -4,7 +4,7 @@ __rpc_to_error = { grpc.StatusCode.ALREADY_EXISTS: errors.CacheExistsError, - grpc.StatusCode.INVALID_ARGUMENT: errors.CacheValueError, + grpc.StatusCode.INVALID_ARGUMENT: errors.InvalidArgumentError, grpc.StatusCode.NOT_FOUND: errors.CacheNotFoundError, grpc.StatusCode.PERMISSION_DENIED: errors.PermissionError, } From a718abc7a1cc47ac7d78856c908b2d42f1e859b8 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Sat, 29 Jan 2022 07:49:56 -0800 Subject: [PATCH 03/14] fix tests --- tests/test_momento_async.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_momento_async.py b/tests/test_momento_async.py index 4f810609..5bf9e8f6 100644 --- a/tests/test_momento_async.py +++ b/tests/test_momento_async.py @@ -87,13 +87,13 @@ async def test_create_cache_throws_exception_for_empty_cache_name(self): async def test_create_cache_throws_validation_exception_for_null_cache_name(self): with self.assertRaises(errors.InvalidInputError) as cm: await self.client.create_cache(None) - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type.") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_create_cache_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.ClientSdkError) as cm: + with self.assertRaises(errors.InvalidInputError) as cm: await self.client.create_cache(1) self.assertEqual('{}'.format(cm.exception), - "Operation failed with error: 1 has type int, but expected one of: bytes, unicode") + "Cache name must be a non-None value with `str` type") async def test_create_cache_throws_permission_exception_for_bad_token(self): async with simple_cache_client.init(_BAD_AUTH_TOKEN, _DEFAULT_TTL_SECONDS) as simple_cache: @@ -234,7 +234,7 @@ async def test_set_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidInputError) as cm: await self.client.set(None, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None string") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_set_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) @@ -286,7 +286,7 @@ async def test_get_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidInputError) as cm: await self.client.get(None, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type.") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_get_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) @@ -301,7 +301,7 @@ async def test_get_with_null_key_throws_exception(self): async def test_get_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidInputError) as cm: await self.client.get(1, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type.") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_get_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidInputError) as cm: From 5ad2c4056bd4fea2b0499a723234f1012865de36 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 08:00:31 -0800 Subject: [PATCH 04/14] rename errors --- .../_cache_service_errors_converter.py | 19 ++++++- src/momento/_momento_endpoint_resolver.py | 2 +- src/momento/_utilities/_data_validation.py | 6 +- src/momento/errors.py | 32 +++++++++-- tests/test_momento.py | 56 +++++++++---------- tests/test_momento_async.py | 56 +++++++++---------- 6 files changed, 104 insertions(+), 67 deletions(-) diff --git a/src/momento/_cache_service_errors_converter.py b/src/momento/_cache_service_errors_converter.py index 99097cb3..11968486 100644 --- a/src/momento/_cache_service_errors_converter.py +++ b/src/momento/_cache_service_errors_converter.py @@ -1,12 +1,25 @@ +from tokenize import group import grpc from . import errors from . import _momento_logger __rpc_to_error = { - grpc.StatusCode.ALREADY_EXISTS: errors.CacheExistsError, - grpc.StatusCode.INVALID_ARGUMENT: errors.InvalidArgumentError, - grpc.StatusCode.NOT_FOUND: errors.CacheNotFoundError, + grpc.StatusCode.INVALID_ARGUMENT: errors.BadRequestError, + grpc.StatusCode.OUT_OF_RANGE: errors.BadRequestError, + grpc.StatusCode.UNIMPLEMENTED: errors.BadRequestError, + grpc.StatusCode.FAILED_PRECONDITION: errors.BadRequestError, + grpc.StatusCode.CANCELLED: errors.CancelError, + grpc.StatusCode.DEADLINE_EXCEEDED: errors.TimeoutError, grpc.StatusCode.PERMISSION_DENIED: errors.PermissionError, + grpc.StatusCode.UNAUTHENTICATED: errors.AuthenticationError, + grpc.StatusCode.RESOURCE_EXHAUSTED: errors.LimitExceededError, + grpc.StatusCode.ALREADY_EXISTS: errors.AlreadyExistsError, + grpc.StatusCode.NOT_FOUND: errors.NotFoundError, + grpc.StatusCode.UNKNOWN: errors.InternalServerError, + grpc.StatusCode.ABORTED: errors.InternalServerError, + grpc.StatusCode.INTERNAL: errors.InternalServerError, + grpc.StatusCode.UNAVAILABLE: errors.InternalServerError, + grpc.StatusCode.DATA_LOSS: errors.InternalServerError, } diff --git a/src/momento/_momento_endpoint_resolver.py b/src/momento/_momento_endpoint_resolver.py index 81cb4e73..c0e629e1 100644 --- a/src/momento/_momento_endpoint_resolver.py +++ b/src/momento/_momento_endpoint_resolver.py @@ -28,4 +28,4 @@ def _getEndpointFromToken(auth_token): return _Endpoints(claims[_CONTROL_ENDPOINT_CLAIM_ID], claims[_CACHE_ENDPOINT_CLAIM_ID]) except (DecodeError, KeyError): - raise errors.InvalidInputError('Invalid Auth token.') from None + raise errors.InvalidArgumentError('Invalid Auth token.') from None diff --git a/src/momento/_utilities/_data_validation.py b/src/momento/_utilities/_data_validation.py index 186a6c75..7fbe8b21 100644 --- a/src/momento/_utilities/_data_validation.py +++ b/src/momento/_utilities/_data_validation.py @@ -9,7 +9,7 @@ def _make_metadata(cache_name) -> Metadata: def _validate_cache_name(cache_name): if cache_name is None or not isinstance(cache_name, str): - raise errors.InvalidInputError('Cache name must be a non-None value with `str` type') + raise errors.InvalidArgumentError('Cache name must be a non-None value with `str` type') def _as_bytes(data, error_message): @@ -17,10 +17,10 @@ def _as_bytes(data, error_message): return data.encode('utf-8') if isinstance(data, bytes): return data - raise errors.InvalidInputError(error_message + str(type(data))) + raise errors.InvalidArgumentError(error_message + str(type(data))) def _validate_ttl(ttl_seconds): if not isinstance(ttl_seconds, int) or ttl_seconds < 0: - raise errors.InvalidInputError( + raise errors.InvalidArgumentError( 'TTL Seconds must be a non-negative integer') diff --git a/src/momento/errors.py b/src/momento/errors.py index 307c004a..297993fa 100644 --- a/src/momento/errors.py +++ b/src/momento/errors.py @@ -15,7 +15,7 @@ def __init__(self, message): super().__init__(message) -class InvalidInputError(ClientSdkError): +class InvalidArgumentError(ClientSdkError): """Error raised when provided input values to the SDK are invalid Some examples - missing required parameters, incorrect parameter @@ -37,30 +37,54 @@ def __init__(self, message): super().__init__(message) -class CacheNotFoundError(CacheServiceError): +class NotFoundError(CacheServiceError): """Error raised for operations performed on non-existent cache""" def __init__(self, message): super().__init__(message) -class CacheExistsError(CacheServiceError): +class AlreadyExistsError(CacheServiceError): """Error raised when attempting to create a cache with same name""" def __init__(self, message): super().__init__(message) -class InvalidArgumentError(CacheServiceError): +class BadRequestError(CacheServiceError): """Error raised when service validation fails for provided values""" def __init__(self, message): super().__init__(message) class PermissionError(CacheServiceError): + """Error for insufficient permissions to perform an operation with Cache Service.""" + def __init__(self, message): + super().__init__(message) + + +class AuthenticationError(CacheServiceError): """Error when authentication with Cache Service fails""" def __init__(self, message): super().__init__(message) +class CancelError(CacheServiceError): + """Error an operation with Cache Service was cancelled""" + def __init__(self, message): + super().__init__(message) + + +class TimeoutError(CacheServiceError): + """Error when an operation did not complete in time""" + def __init__(self, message): + super().__init__(message) + + +class LimitExceededError(CacheServiceError): + """Error when calls are throttled due to request limit rate""" + def __init__(self, message): + super().__init__(message) + + class InternalServerError(CacheServiceError): """Operation failed on the server with an unknown error""" def __init__(self, message): diff --git a/tests/test_momento.py b/tests/test_momento.py index 50b5ee2e..d77748c9 100644 --- a/tests/test_momento.py +++ b/tests/test_momento.py @@ -31,7 +31,7 @@ def setUpClass(cls): # ensure test cache exists try: cls.client.create_cache(_TEST_CACHE_NAME) - except errors.CacheExistsError: + except errors.AlreadyExistsError: # do nothing, cache already exists pass @@ -64,32 +64,32 @@ def test_create_cache_get_set_values_and_delete_cache(self): # init def test_init_throws_exception_when_client_uses_negative_default_ttl(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: simple_cache_client.init(_AUTH_TOKEN, -1) self.assertEqual('{}'.format(cm.exception), "TTL Seconds must be a non-negative integer") def test_init_throws_exception_for_non_jwt_token(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: simple_cache_client.init("notanauthtoken", _DEFAULT_TTL_SECONDS) self.assertEqual('{}'.format(cm.exception), "Invalid Auth token.") # create_cache def test_create_cache_throws_already_exists_when_creating_existing_cache(self): - with self.assertRaises(errors.CacheExistsError): + with self.assertRaises(errors.AlreadyExistsError): self.client.create_cache(_TEST_CACHE_NAME) def test_create_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.InvalidArgumentError): + with self.assertRaises(errors.BadRequestError): self.client.create_cache("") def test_create_cache_throws_validation_exception_for_null_cache_name(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.create_cache(None) self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_create_cache_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.create_cache(1) self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") @@ -104,27 +104,27 @@ def test_delete_cache_succeeds(self): cache_name = str(uuid.uuid4()) self.client.create_cache(cache_name) - with self.assertRaises(errors.CacheExistsError): + with self.assertRaises(errors.AlreadyExistsError): self.client.create_cache(cache_name) self.client.delete_cache(cache_name) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): self.client.delete_cache(cache_name) def test_delete_cache_throws_not_found_when_deleting_unknown_cache(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): self.client.delete_cache(cache_name) def test_delete_cache_throws_invalid_input_for_null_cache_name(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): self.client.delete_cache(None) def test_delete_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.InvalidArgumentError): + with self.assertRaises(errors.BadRequestError): self.client.delete_cache("") def test_delete_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.delete_cache(1) self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") @@ -229,46 +229,46 @@ def test_set_with_different_ttl(self): def test_set_with_non_existent_cache_name_throws_not_found(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): self.client.set(cache_name, "foo", "bar") def test_set_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set(None, "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_set_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidArgumentError) as cm: + with self.assertRaises(errors.BadRequestError) as cm: self.client.set("", "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") def test_set_with_null_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): self.client.set(_TEST_CACHE_NAME, None, "bar") def test_set_with_null_value_throws_exception(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): self.client.set(_TEST_CACHE_NAME, "foo", None) def test_set_negative_ttl_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set(_TEST_CACHE_NAME, "foo", "bar", -1) self.assertEqual('{}'.format(cm.exception), "TTL Seconds must be a non-negative integer") def test_set_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set(1, "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_set_with_bad_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set(_TEST_CACHE_NAME, 1, "bar") self.assertEqual('{}'.format(cm.exception), "Unsupported type for key: ") def test_set_with_bad_value_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set(_TEST_CACHE_NAME, "foo", 1) self.assertEqual('{}'.format(cm.exception), "Unsupported type for value: ") @@ -282,32 +282,32 @@ def test_set_throws_permission_exception_for_bad_token(self): def test_get_with_non_existent_cache_name_throws_not_found(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): self.client.get(cache_name, "foo") def test_get_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.get(None, "foo") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_get_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidArgumentError) as cm: + with self.assertRaises(errors.BadRequestError) as cm: self.client.get("", "foo") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") def test_get_with_null_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): self.client.get(_TEST_CACHE_NAME, None) def test_get_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.get(1, "foo") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") def test_get_with_bad_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.get(_TEST_CACHE_NAME, 1) self.assertEqual('{}'.format(cm.exception), "Unsupported type for key: ") diff --git a/tests/test_momento_async.py b/tests/test_momento_async.py index 5bf9e8f6..bccbd675 100644 --- a/tests/test_momento_async.py +++ b/tests/test_momento_async.py @@ -31,7 +31,7 @@ async def asyncSetUp(self) -> None: # ensure test cache exists try: await self.client.create_cache(_TEST_CACHE_NAME) - except errors.CacheExistsError: + except errors.AlreadyExistsError: # do nothing, cache already exists pass @@ -65,32 +65,32 @@ async def test_create_cache_get_set_values_and_delete_cache(self): # init async def test_init_throws_exception_when_client_uses_negative_default_ttl(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: simple_cache_client.init(_AUTH_TOKEN, -1) self.assertEqual('{}'.format(cm.exception), "TTL Seconds must be a non-negative integer") async def test_init_throws_exception_for_non_jwt_token(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: simple_cache_client.init("notanauthtoken", _DEFAULT_TTL_SECONDS) self.assertEqual('{}'.format(cm.exception), "Invalid Auth token.") # create_cache async def test_create_cache_throws_already_exists_when_creating_existing_cache(self): - with self.assertRaises(errors.CacheExistsError): + with self.assertRaises(errors.AlreadyExistsError): await self.client.create_cache(_TEST_CACHE_NAME) async def test_create_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.InvalidArgumentError): + with self.assertRaises(errors.BadRequestError): await self.client.create_cache("") async def test_create_cache_throws_validation_exception_for_null_cache_name(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.create_cache(None) self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_create_cache_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.create_cache(1) self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") @@ -105,27 +105,27 @@ async def test_delete_cache_succeeds(self): cache_name = str(uuid.uuid4()) await self.client.create_cache(cache_name) - with self.assertRaises(errors.CacheExistsError): + with self.assertRaises(errors.AlreadyExistsError): await self.client.create_cache(cache_name) await self.client.delete_cache(cache_name) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): await self.client.delete_cache(cache_name) async def test_delete_cache_throws_not_found_when_deleting_unknown_cache(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): await self.client.delete_cache(cache_name) async def test_delete_cache_throws_invalid_input_for_null_cache_name(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): await self.client.delete_cache(None) async def test_delete_cache_throws_exception_for_empty_cache_name(self): - with self.assertRaises(errors.InvalidArgumentError): + with self.assertRaises(errors.BadRequestError): await self.client.delete_cache("") async def test_delete_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.delete_cache(1) self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") @@ -227,46 +227,46 @@ async def test_set_with_different_ttl(self): async def test_set_with_non_existent_cache_name_throws_not_found(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): await self.client.set(cache_name, "foo", "bar") async def test_set_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set(None, "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_set_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidArgumentError) as cm: + with self.assertRaises(errors.BadRequestError) as cm: await self.client.set("", "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") async def test_set_with_null_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): await self.client.set(_TEST_CACHE_NAME, None, "bar") async def test_set_with_null_value_throws_exception(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): await self.client.set(_TEST_CACHE_NAME, "foo", None) async def test_set_negative_ttl_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set(_TEST_CACHE_NAME, "foo", "bar", -1) self.assertEqual('{}'.format(cm.exception), "TTL Seconds must be a non-negative integer") async def test_set_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set(1, "foo", "bar") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_set_with_bad_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set(_TEST_CACHE_NAME, 1, "bar") self.assertEqual('{}'.format(cm.exception), "Unsupported type for key: ") async def test_set_with_bad_value_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set(_TEST_CACHE_NAME, "foo", 1) self.assertEqual('{}'.format(cm.exception), "Unsupported type for value: ") @@ -279,32 +279,32 @@ async def test_set_throws_permission_exception_for_bad_token(self): async def test_get_with_non_existent_cache_name_throws_not_found(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.CacheNotFoundError): + with self.assertRaises(errors.NotFoundError): await self.client.get(cache_name, "foo") async def test_get_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.get(None, "foo") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_get_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) - with self.assertRaises(errors.InvalidArgumentError) as cm: + with self.assertRaises(errors.BadRequestError) as cm: await self.client.get("", "foo") self.assertEqual('{}'.format(cm.exception), "Cache header is empty") async def test_get_with_null_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError): + with self.assertRaises(errors.InvalidArgumentError): await self.client.get(_TEST_CACHE_NAME, None) async def test_get_with_bad_cache_name_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.get(1, "foo") self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") async def test_get_with_bad_key_throws_exception(self): - with self.assertRaises(errors.InvalidInputError) as cm: + with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.get(_TEST_CACHE_NAME, 1) self.assertEqual('{}'.format(cm.exception), "Unsupported type for key: ") From 38a961c9c6e12dafa8ab4f8a7754db45cd03f974 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 08:02:09 -0800 Subject: [PATCH 05/14] remove unwanted import --- src/momento/_cache_service_errors_converter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/momento/_cache_service_errors_converter.py b/src/momento/_cache_service_errors_converter.py index 11968486..703be303 100644 --- a/src/momento/_cache_service_errors_converter.py +++ b/src/momento/_cache_service_errors_converter.py @@ -1,4 +1,3 @@ -from tokenize import group import grpc from . import errors from . import _momento_logger From a2b84003af6a9e22ebf76caf3ceee6d309a17a58 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 11:05:58 -0800 Subject: [PATCH 06/14] update check --- src/momento/_utilities/_data_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/momento/_utilities/_data_validation.py b/src/momento/_utilities/_data_validation.py index 7fbe8b21..cbcea264 100644 --- a/src/momento/_utilities/_data_validation.py +++ b/src/momento/_utilities/_data_validation.py @@ -8,7 +8,7 @@ def _make_metadata(cache_name) -> Metadata: def _validate_cache_name(cache_name): - if cache_name is None or not isinstance(cache_name, str): + if not cache_name or not isinstance(cache_name, str): raise errors.InvalidArgumentError('Cache name must be a non-None value with `str` type') From 77aaab78b3454ba4fa66a38ad869cc4f2202a28e Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 11:34:03 -0800 Subject: [PATCH 07/14] fix: use None check, not seems to checking empty str that we don't want to check on SDK --- src/momento/_utilities/_data_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/momento/_utilities/_data_validation.py b/src/momento/_utilities/_data_validation.py index cbcea264..7fbe8b21 100644 --- a/src/momento/_utilities/_data_validation.py +++ b/src/momento/_utilities/_data_validation.py @@ -8,7 +8,7 @@ def _make_metadata(cache_name) -> Metadata: def _validate_cache_name(cache_name): - if not cache_name or not isinstance(cache_name, str): + if cache_name is None or not isinstance(cache_name, str): raise errors.InvalidArgumentError('Cache name must be a non-None value with `str` type') From 6d2a0e24aaa7b70ad553562e764f42870bcb2cbe Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 11:53:01 -0800 Subject: [PATCH 08/14] fix: doc strings --- src/momento/aio/simple_cache_client.py | 40 ++++++++++++-------------- src/momento/simple_cache_client.py | 40 ++++++++++++-------------- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/momento/aio/simple_cache_client.py b/src/momento/aio/simple_cache_client.py index 37a9d247..205fd841 100644 --- a/src/momento/aio/simple_cache_client.py +++ b/src/momento/aio/simple_cache_client.py @@ -33,11 +33,11 @@ async def create_cache(self, cache_name) -> CreateCacheResponse: CreateCacheResponse Raises: - InvalidInputError: If cache name is None. + InvalidArgumentError: If provided cache_name is empty or None. + BadRequestError: If the cache name provided doesn't follow the naming conventions + ExistsError: If cache with the given name already exists. + AuthenticationError: If the provided Momento Auth Token is invalid. ClientSdkError: For any SDK checks that fail. - CacheValueError: If provided cache_name is empty. - CacheExistsError: If cache with the given name already exists. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. """ return await self._control_client.create_cache(cache_name) @@ -51,11 +51,11 @@ async def delete_cache(self, cache_name) -> DeleteCacheResponse: DeleteCacheResponse Raises: - CacheNotFoundError: If an attempt is made to delete a MomentoCache that doesn't exits. - InvalidInputError: If cache name is None. + InvalidArgumentError: If provided cache_name is empty or None. + BadRequestError: If the cache name provided doesn't follow the naming conventions + NotFoundError: If an attempt is made to delete a MomentoCache that doesn't exits. + AuthenticationError: If the provided Momento Auth Token is invalid. ClientSdkError: For any SDK checks that fail. - CacheValueError: If provided cache name is empty - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. """ return await self._control_client.delete_cache(cache_name) @@ -69,8 +69,7 @@ async def list_caches(self, next_token=None) -> ListCachesResponse: ListCachesResponse Raises: - Exception to notify either sdk, grpc, or operation error. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. + AuthenticationError: If the provided Momento Auth Token is invalid. """ return await self._control_client.list_caches(next_token) @@ -87,10 +86,10 @@ async def set(self, cache_name, key, value, ttl_seconds=None) -> CacheSetRespons CacheSetResponse Raises: - InvalidInputError: If service validation fails for provided values. - ClientSdkError: If cache name is invalid type. - CacheNotFoundError: If an attempt is made to store an item in a cache that doesn't exist. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. + InvalidArgumentError: If validation fails for provided values. + BadRequestError: If the provided inputs are rejected by server because they are invalid + NotFoundError: If the cache with the given name doesn't exist. + AuthenticationError: If the provided Momento Auth Token is invalid. InternalServerError: If server encountered an unknown error while trying to store the item. """ return await self._data_client.set(cache_name, key, value, ttl_seconds) @@ -106,11 +105,11 @@ async def get(self, cache_name, key) -> CacheGetResponse: CacheGetResponse Raises: - InvalidInputError: If service validation fails for provided values. - ClientSdkError: If cache name is invalid type. - CacheNotFoundError: If an attempt is made to retrieve an item in a cache that doesn't exist. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. - InternalServerError: If server encountered an unknown error while trying to retrieve the item. + InvalidArgumentError: If validation fails for provided values. + BadRequestError: If the provided inputs are rejected by server because they are invalid + NotFoundError: If the cache with the given name doesn't exist. + AuthenticationError: If the provided Momento Auth Token is invalid. + InternalServerError: If server encountered an unknown error while trying to store the item. """ return await self._data_client.get(cache_name, key) @@ -124,7 +123,6 @@ def init(auth_token, item_default_ttl_seconds) -> SimpleCacheClient: Returns: SimpleCacheClient Raises: - InvalidInputError: If service validation fails for provided values - InternalServerError: If server encountered an unknown error. + IllegalArgumentError: If the provided auth token and/or item_default_ttl_seconds is invalid """ return SimpleCacheClient(auth_token, item_default_ttl_seconds) diff --git a/src/momento/simple_cache_client.py b/src/momento/simple_cache_client.py index ef933130..c01ddc69 100644 --- a/src/momento/simple_cache_client.py +++ b/src/momento/simple_cache_client.py @@ -31,11 +31,11 @@ def create_cache(self, cache_name) -> CreateCacheResponse: CreateCacheResponse Raises: - InvalidInputError: If cache name is None. + InvalidArgumentError: If provided cache_name is empty or None. + BadRequestError: If the cache name provided doesn't follow the naming conventions + ExistsError: If cache with the given name already exists. + AuthenticationError: If the provided Momento Auth Token is invalid. ClientSdkError: For any SDK checks that fail. - InvalidArgumentError: If provided cache_name is empty. - CacheExistsError: If cache with the given name already exists. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. """ return self._control_client.create_cache(cache_name) @@ -49,11 +49,11 @@ def delete_cache(self, cache_name) -> DeleteCacheResponse: DeleteCacheResponse Raises: - CacheNotFoundError: If an attempt is made to delete a MomentoCache that doesn't exits. - InvalidInputError: If cache name is None. + InvalidArgumentError: If provided cache_name is empty or None. + BadRequestError: If the cache name provided doesn't follow the naming conventions + NotFoundError: If an attempt is made to delete a MomentoCache that doesn't exits. + AuthenticationError: If the provided Momento Auth Token is invalid. ClientSdkError: For any SDK checks that fail. - InvalidArgumentError: If provided cache name is empty - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. """ return self._control_client.delete_cache(cache_name) @@ -67,8 +67,7 @@ def list_caches(self, next_token=None) -> ListCachesResponse: ListCachesResponse Raises: - Exception to notify either sdk, grpc, or operation error. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. + AuthenticationError: If the provided Momento Auth Token is invalid. """ return self._control_client.list_caches(next_token) @@ -85,10 +84,10 @@ def set(self, cache_name, key, value, ttl_seconds=None) -> CacheSetResponse: CacheSetResponse Raises: - InvalidInputError: If service validation fails for provided values. - ClientSdkError: If cache name is invalid type. - CacheNotFoundError: If an attempt is made to store an item in a cache that doesn't exist. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. + InvalidArgumentError: If validation fails for provided values. + BadRequestError: If the provided inputs are rejected by server because they are invalid + NotFoundError: If the cache with the given name doesn't exist. + AuthenticationError: If the provided Momento Auth Token is invalid. InternalServerError: If server encountered an unknown error while trying to store the item. """ return self._data_client.set(cache_name, key, value, ttl_seconds) @@ -104,11 +103,11 @@ def get(self, cache_name, key) -> CacheGetResponse: CacheGetResponse Raises: - InvalidInputError: If service validation fails for provided values. - ClientSdkError: If cache name is invalid type. - CacheNotFoundError: If an attempt is made to retrieve an item in a cache that doesn't exist. - PermissionError: If the provided Momento Auth Token is invalid to perform the requested operation. - InternalServerError: If server encountered an unknown error while trying to retrieve the item. + InvalidArgumentError: If validation fails for provided values. + BadRequestError: If the provided inputs are rejected by server because they are invalid + NotFoundError: If the cache with the given name doesn't exist. + AuthenticationError: If the provided Momento Auth Token is invalid. + InternalServerError: If server encountered an unknown error while trying to store the item. """ return self._data_client.get(cache_name, key) @@ -122,7 +121,6 @@ def init(auth_token, item_default_ttl_seconds) -> SimpleCacheClient: Returns: SimpleCacheClient Raises: - InvalidInputError: If service validation fails for provided values - InternalServerError: If server encountered an unknown error. + IllegalArgumentError: If the provided auth token and/or item_default_ttl_seconds is invalid """ return SimpleCacheClient(auth_token, item_default_ttl_seconds) From 37dd8fae180985903103c6c8fe37a45053a94025 Mon Sep 17 00:00:00 2001 From: gautamomento <89037104+gautamomento@users.noreply.github.com> Date: Tue, 1 Feb 2022 11:57:49 -0800 Subject: [PATCH 09/14] Update src/momento/errors.py Co-authored-by: Ruth Linehan <1530016+rlinehan@users.noreply.github.com> --- src/momento/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/momento/errors.py b/src/momento/errors.py index 297993fa..e6ab25aa 100644 --- a/src/momento/errors.py +++ b/src/momento/errors.py @@ -68,7 +68,7 @@ def __init__(self, message): class CancelError(CacheServiceError): - """Error an operation with Cache Service was cancelled""" + """Error when an operation with Cache Service was cancelled""" def __init__(self, message): super().__init__(message) From 2ac93339f32cf56392e612b0a5b2edfd56292117 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 11:59:45 -0800 Subject: [PATCH 10/14] fix docstrings --- src/momento/aio/simple_cache_client.py | 6 +++--- src/momento/simple_cache_client.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/momento/aio/simple_cache_client.py b/src/momento/aio/simple_cache_client.py index 205fd841..9935184c 100644 --- a/src/momento/aio/simple_cache_client.py +++ b/src/momento/aio/simple_cache_client.py @@ -86,7 +86,7 @@ async def set(self, cache_name, key, value, ttl_seconds=None) -> CacheSetRespons CacheSetResponse Raises: - InvalidArgumentError: If validation fails for provided values. + InvalidArgumentError: If validation fails for provided method arguments. BadRequestError: If the provided inputs are rejected by server because they are invalid NotFoundError: If the cache with the given name doesn't exist. AuthenticationError: If the provided Momento Auth Token is invalid. @@ -105,11 +105,11 @@ async def get(self, cache_name, key) -> CacheGetResponse: CacheGetResponse Raises: - InvalidArgumentError: If validation fails for provided values. + InvalidArgumentError: If validation fails for provided method arguments. BadRequestError: If the provided inputs are rejected by server because they are invalid NotFoundError: If the cache with the given name doesn't exist. AuthenticationError: If the provided Momento Auth Token is invalid. - InternalServerError: If server encountered an unknown error while trying to store the item. + InternalServerError: If server encountered an unknown error while trying to retrieve the item. """ return await self._data_client.get(cache_name, key) diff --git a/src/momento/simple_cache_client.py b/src/momento/simple_cache_client.py index c01ddc69..49ee810c 100644 --- a/src/momento/simple_cache_client.py +++ b/src/momento/simple_cache_client.py @@ -84,7 +84,7 @@ def set(self, cache_name, key, value, ttl_seconds=None) -> CacheSetResponse: CacheSetResponse Raises: - InvalidArgumentError: If validation fails for provided values. + InvalidArgumentError: If validation fails for the provided method arguments. BadRequestError: If the provided inputs are rejected by server because they are invalid NotFoundError: If the cache with the given name doesn't exist. AuthenticationError: If the provided Momento Auth Token is invalid. @@ -103,11 +103,11 @@ def get(self, cache_name, key) -> CacheGetResponse: CacheGetResponse Raises: - InvalidArgumentError: If validation fails for provided values. + InvalidArgumentError: If validation fails for the provided method arguments. BadRequestError: If the provided inputs are rejected by server because they are invalid NotFoundError: If the cache with the given name doesn't exist. AuthenticationError: If the provided Momento Auth Token is invalid. - InternalServerError: If server encountered an unknown error while trying to store the item. + InternalServerError: If server encountered an unknown error while trying to retrieve the item. """ return self._data_client.get(cache_name, key) From ffa2e112676098954800ee9d349248eaaa362af4 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 13:11:17 -0800 Subject: [PATCH 11/14] rename exception --- src/momento/_cache_service_errors_converter.py | 2 +- src/momento/errors.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/momento/_cache_service_errors_converter.py b/src/momento/_cache_service_errors_converter.py index 703be303..d4d82535 100644 --- a/src/momento/_cache_service_errors_converter.py +++ b/src/momento/_cache_service_errors_converter.py @@ -7,7 +7,7 @@ grpc.StatusCode.OUT_OF_RANGE: errors.BadRequestError, grpc.StatusCode.UNIMPLEMENTED: errors.BadRequestError, grpc.StatusCode.FAILED_PRECONDITION: errors.BadRequestError, - grpc.StatusCode.CANCELLED: errors.CancelError, + grpc.StatusCode.CANCELLED: errors.CancelledError, grpc.StatusCode.DEADLINE_EXCEEDED: errors.TimeoutError, grpc.StatusCode.PERMISSION_DENIED: errors.PermissionError, grpc.StatusCode.UNAUTHENTICATED: errors.AuthenticationError, diff --git a/src/momento/errors.py b/src/momento/errors.py index e6ab25aa..4f5fe8e6 100644 --- a/src/momento/errors.py +++ b/src/momento/errors.py @@ -67,7 +67,7 @@ def __init__(self, message): super().__init__(message) -class CancelError(CacheServiceError): +class CancelledError(CacheServiceError): """Error when an operation with Cache Service was cancelled""" def __init__(self, message): super().__init__(message) From d0cb7ae25de75875d5035ca13928a2098c4e5704 Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 13:13:20 -0800 Subject: [PATCH 12/14] update docstring --- src/momento/aio/simple_cache_client.py | 4 ++-- src/momento/simple_cache_client.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/momento/aio/simple_cache_client.py b/src/momento/aio/simple_cache_client.py index 9935184c..323497c3 100644 --- a/src/momento/aio/simple_cache_client.py +++ b/src/momento/aio/simple_cache_client.py @@ -33,7 +33,7 @@ async def create_cache(self, cache_name) -> CreateCacheResponse: CreateCacheResponse Raises: - InvalidArgumentError: If provided cache_name is empty or None. + InvalidArgumentError: If provided cache_name None. BadRequestError: If the cache name provided doesn't follow the naming conventions ExistsError: If cache with the given name already exists. AuthenticationError: If the provided Momento Auth Token is invalid. @@ -51,7 +51,7 @@ async def delete_cache(self, cache_name) -> DeleteCacheResponse: DeleteCacheResponse Raises: - InvalidArgumentError: If provided cache_name is empty or None. + InvalidArgumentError: If provided cache_name is None. BadRequestError: If the cache name provided doesn't follow the naming conventions NotFoundError: If an attempt is made to delete a MomentoCache that doesn't exits. AuthenticationError: If the provided Momento Auth Token is invalid. diff --git a/src/momento/simple_cache_client.py b/src/momento/simple_cache_client.py index 49ee810c..323cdb76 100644 --- a/src/momento/simple_cache_client.py +++ b/src/momento/simple_cache_client.py @@ -31,7 +31,7 @@ def create_cache(self, cache_name) -> CreateCacheResponse: CreateCacheResponse Raises: - InvalidArgumentError: If provided cache_name is empty or None. + InvalidArgumentError: If provided cache_name is None. BadRequestError: If the cache name provided doesn't follow the naming conventions ExistsError: If cache with the given name already exists. AuthenticationError: If the provided Momento Auth Token is invalid. @@ -49,7 +49,7 @@ def delete_cache(self, cache_name) -> DeleteCacheResponse: DeleteCacheResponse Raises: - InvalidArgumentError: If provided cache_name is empty or None. + InvalidArgumentError: If provided cache_name is None. BadRequestError: If the cache name provided doesn't follow the naming conventions NotFoundError: If an attempt is made to delete a MomentoCache that doesn't exits. AuthenticationError: If the provided Momento Auth Token is invalid. From 09a4f68d23247d7abc94ccb87fb73a04da0d4549 Mon Sep 17 00:00:00 2001 From: gautamomento <89037104+gautamomento@users.noreply.github.com> Date: Tue, 1 Feb 2022 13:28:15 -0800 Subject: [PATCH 13/14] Update src/momento/simple_cache_client.py Co-authored-by: Ruth Linehan <1530016+rlinehan@users.noreply.github.com> --- src/momento/simple_cache_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/momento/simple_cache_client.py b/src/momento/simple_cache_client.py index 323cdb76..a4598fa0 100644 --- a/src/momento/simple_cache_client.py +++ b/src/momento/simple_cache_client.py @@ -33,7 +33,7 @@ def create_cache(self, cache_name) -> CreateCacheResponse: Raises: InvalidArgumentError: If provided cache_name is None. BadRequestError: If the cache name provided doesn't follow the naming conventions - ExistsError: If cache with the given name already exists. + AlreadyExistsError: If cache with the given name already exists. AuthenticationError: If the provided Momento Auth Token is invalid. ClientSdkError: For any SDK checks that fail. """ From e4e9718736aed10954485209573f07e886b87e0c Mon Sep 17 00:00:00 2001 From: gautamomento Date: Tue, 1 Feb 2022 13:29:39 -0800 Subject: [PATCH 14/14] fix error message --- src/momento/_utilities/_data_validation.py | 2 +- tests/test_momento.py | 14 +++++++------- tests/test_momento_async.py | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/momento/_utilities/_data_validation.py b/src/momento/_utilities/_data_validation.py index 7fbe8b21..33e471e0 100644 --- a/src/momento/_utilities/_data_validation.py +++ b/src/momento/_utilities/_data_validation.py @@ -9,7 +9,7 @@ def _make_metadata(cache_name) -> Metadata: def _validate_cache_name(cache_name): if cache_name is None or not isinstance(cache_name, str): - raise errors.InvalidArgumentError('Cache name must be a non-None value with `str` type') + raise errors.InvalidArgumentError('Cache name must be a non-empty string') def _as_bytes(data, error_message): diff --git a/tests/test_momento.py b/tests/test_momento.py index d77748c9..bd0ac54b 100644 --- a/tests/test_momento.py +++ b/tests/test_momento.py @@ -86,12 +86,12 @@ def test_create_cache_throws_exception_for_empty_cache_name(self): def test_create_cache_throws_validation_exception_for_null_cache_name(self): with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.create_cache(None) - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") def test_create_cache_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.create_cache(1) - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") def test_create_cache_throws_permission_exception_for_bad_token(self): with simple_cache_client.init(_BAD_AUTH_TOKEN, @@ -126,7 +126,7 @@ def test_delete_cache_throws_exception_for_empty_cache_name(self): def test_delete_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.delete_cache(1) - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") def test_delete_cache_throws_permission_exception_for_bad_token(self): with simple_cache_client.init(_BAD_AUTH_TOKEN, @@ -236,7 +236,7 @@ def test_set_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set(None, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") def test_set_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) @@ -260,7 +260,7 @@ def test_set_negative_ttl_throws_exception(self): def test_set_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.set(1, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") def test_set_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: @@ -289,7 +289,7 @@ def test_get_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.get(None, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") def test_get_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) @@ -304,7 +304,7 @@ def test_get_with_null_key_throws_exception(self): def test_get_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: self.client.get(1, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") def test_get_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: diff --git a/tests/test_momento_async.py b/tests/test_momento_async.py index bccbd675..de934c33 100644 --- a/tests/test_momento_async.py +++ b/tests/test_momento_async.py @@ -87,13 +87,13 @@ async def test_create_cache_throws_exception_for_empty_cache_name(self): async def test_create_cache_throws_validation_exception_for_null_cache_name(self): with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.create_cache(None) - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") async def test_create_cache_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.create_cache(1) self.assertEqual('{}'.format(cm.exception), - "Cache name must be a non-None value with `str` type") + "Cache name must be a non-empty string") async def test_create_cache_throws_permission_exception_for_bad_token(self): async with simple_cache_client.init(_BAD_AUTH_TOKEN, _DEFAULT_TTL_SECONDS) as simple_cache: @@ -127,7 +127,7 @@ async def test_delete_cache_throws_exception_for_empty_cache_name(self): async def test_delete_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.delete_cache(1) - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") async def test_delete_cache_throws_permission_exception_for_bad_token(self): async with simple_cache_client.init(_BAD_AUTH_TOKEN, _DEFAULT_TTL_SECONDS) as simple_cache: @@ -234,7 +234,7 @@ async def test_set_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set(None, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") async def test_set_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) @@ -258,7 +258,7 @@ async def test_set_negative_ttl_throws_exception(self): async def test_set_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.set(1, "foo", "bar") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") async def test_set_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: @@ -286,7 +286,7 @@ async def test_get_with_null_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.get(None, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") async def test_get_with_empty_cache_name_throws_exception(self): cache_name = str(uuid.uuid4()) @@ -301,7 +301,7 @@ async def test_get_with_null_key_throws_exception(self): async def test_get_with_bad_cache_name_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: await self.client.get(1, "foo") - self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-None value with `str` type") + self.assertEqual('{}'.format(cm.exception), "Cache name must be a non-empty string") async def test_get_with_bad_key_throws_exception(self): with self.assertRaises(errors.InvalidArgumentError) as cm: