diff --git a/src/mega/__init__.py b/src/mega/__init__.py index 48bd366..d79c08a 100644 --- a/src/mega/__init__.py +++ b/src/mega/__init__.py @@ -1 +1,2 @@ from .mega import Mega # noqa +from . import errors diff --git a/src/mega/errors.py b/src/mega/errors.py index 4df7f90..cbbc915 100644 --- a/src/mega/errors.py +++ b/src/mega/errors.py @@ -5,83 +5,154 @@ class ValidationError(Exception): pass -_CODE_TO_DESCRIPTIONS = { - -1: ( - 'EINTERNAL', - ( - 'An internal error has occurred. Please submit a bug report, ' - 'detailing the exact circumstances in which this error occurred' - ) - ), - -2: ('EARGS', 'You have passed invalid arguments to this command'), - -3: ( - 'EAGAIN', - ( - '(always at the request level) A temporary congestion or server ' - 'malfunction prevented your request from being processed. ' - 'No data was altered. Retry. Retries must be spaced with ' - 'exponential backoff' - ) - ), - -4: ( - 'ERATELIMIT', - ( - 'You have exceeded your command weight per time quota. Please ' - 'wait a few seconds, then try again (this should never happen ' - 'in sane real-life applications)' - ) - ), - -5: ('EFAILED', 'The upload failed. Please restart it from scratch'), - -6: ( - 'ETOOMANY', - 'Too many concurrent IP addresses are accessing this upload target URL' - ), - -7: ( - 'ERANGE', - ( - 'The upload file packet is out of range or not starting and ' - 'ending on a chunk boundary' - ) - ), - -8: ( - 'EEXPIRED', - ( - 'The upload target URL you are trying to access has expired. ' - 'Please request a fresh one' - ) - ), - -9: ('ENOENT', 'Object (typically, node or user) not found'), - -10: ('ECIRCULAR', 'Circular linkage attempted'), - -11: ( - 'EACCESS', - 'Access violation (e.g., trying to write to a read-only share)' - ), - -12: ('EEXIST', 'Trying to create an object that already exists'), - -13: ('EINCOMPLETE', 'Trying to access an incomplete resource'), - -14: ('EKEY', 'A decryption operation failed (never returned by the API)'), - -15: ('ESID', 'Invalid or expired user session, please relogin'), - -16: ('EBLOCKED', 'User blocked'), - -17: ('EOVERQUOTA', 'Request over quota'), - -18: ( - 'ETEMPUNAVAIL', - 'Resource temporarily not available, please try again later' - ), - -19: ('ETOOMANYCONNECTIONS', 'many connections on this resource'), - -20: ('EWRITE', 'Write failed'), - -21: ('EREAD', 'Read failed'), - -22: ('EAPPKEY', 'Invalid application key; request not processed'), -} - - class RequestError(Exception): """ Error in API request """ - def __init__(self, message): - code = message - self.code = code - code_desc, long_desc = _CODE_TO_DESCRIPTIONS[code] - self.message = f'{code_desc}, {long_desc}' + def __init__(self, message=None): + # If you need to raise a generic RequestError with a custom message, + # use this constructor. Otherwise you can use error_for_code. + if message is not None: + self.message = message def __str__(self): return self.message + + +class EINTERNAL(RequestError): + code = -1 + message = ( + 'An internal error has occurred. Please submit a bug report, detailing ' + 'the exact circumstances in which this error occurred' + ) + +class EARGS(RequestError): + code = -2 + message = 'You have passed invalid arguments to this command' + +class EAGAIN(RequestError): + code = -3 + message = ( + '(always at the request level) A temporary congestion or server ' + 'malfunction prevented your request from being processed. No data was ' + 'altered. Retry. Retries must be spaced with exponential backoff' + ) + +class ERATELIMIT(RequestError): + code = -4 + message = ( + 'You have exceeded your command weight per time quota. Please wait a ' + 'few seconds, then try again (this should never happen in sane ' + 'real-life applications)' + ) + +class EFAILED(RequestError): + code = -5 + message = 'The upload failed. Please restart it from scratch' + +class ETOOMANY(RequestError): + code = -6 + message = ( + 'Too many concurrent IP addresses are accessing this upload target URL' + ) + +class ERANGE(RequestError): + code = -7 + message = ( + 'The upload file packet is out of range or not starting and ending on ' + 'a chunk boundary' + ) + +class EEXPIRED(RequestError): + code = -8 + message = ( + 'The upload target URL you are trying to access has expired. Please ' + 'request a fresh one' + ) + +class ENOENT(RequestError): + code = -9 + message = 'Object (typically, node or user) not found' + +class ECIRCULAR(RequestError): + code = -10 + message = 'Circular linkage attempted' + +class EACCESS(RequestError): + code = -11 + message = 'Access violation (e.g., trying to write to a read-only share)' + +class EEXIST(RequestError): + code = -12 + message = 'Trying to create an object that already exists' + +class EINCOMPLETE(RequestError): + code = -13 + message = 'Trying to access an incomplete resource' + +class EKEY(RequestError): + code = -14 + message = 'A decryption operation failed (never returned by the API)' + +class ESID(RequestError): + code = -15 + message = 'Invalid or expired user session, please relogin' + +class EBLOCKED(RequestError): + code = -16 + message = 'User blocked' + +class EOVERQUOTA(RequestError): + code = -17 + message = 'Request over quota' + +class ETEMPUNAVAIL(RequestError): + code = -18 + message = 'Resource temporarily not available, please try again later' + +class ETOOMANYCONNECTIONS(RequestError): + code = -19 + message = 'many connections on this resource' + +class EWRITE(RequestError): + code = -20 + message = 'Write failed' + +class EREAD(RequestError): + code = -21 + message = 'Read failed' + +class EAPPKEY(RequestError): + code = -22 + message = 'Invalid application key; request not processed' + + +_CODE_TO_CLASSES = { + -1: EINTERNAL, + -2: EARGS, + -3: EAGAIN, + -4: ERATELIMIT, + -5: EFAILED, + -6: ETOOMANY, + -7: ERANGE, + -8: EEXPIRED, + -9: ENOENT, + -10: ECIRCULAR, + -11: EACCESS, + -12: EEXIST, + -13: EINCOMPLETE, + -14: EKEY, + -15: ESID, + -16: EBLOCKED, + -17: EOVERQUOTA, + -18: ETEMPUNAVAIL, + -19: ETOOMANYCONNECTIONS, + -20: EWRITE, + -21: EREAD, + -22: EAPPKEY, +} + + +def error_for_code(code): + cls = _CODE_TO_CLASSES[code] + return cls() diff --git a/src/mega/mega.py b/src/mega/mega.py index 93b5fed..b0915d2 100644 --- a/src/mega/mega.py +++ b/src/mega/mega.py @@ -17,7 +17,7 @@ import shutil import requests from tenacity import retry, wait_exponential, retry_if_exception_type -from .errors import ValidationError, RequestError +from .errors import ValidationError, RequestError, error_for_code from .crypto import ( a32_to_base64, encrypt_key, base64_url_encode, encrypt_attr, base64_to_a32, base64_url_decode, decrypt_attr, a32_to_str, get_chunks, str_to_a32, @@ -186,7 +186,7 @@ class Mega: msg = 'Request failed, retrying' logger.info(msg) raise RuntimeError(msg) - raise RequestError(json_resp) + raise error_for_code(json_resp) return json_resp[0] def _parse_url(self, url):