<gh_stars>1-10
class CommunicationError(Exception):
    """Custom error handling class to adapt messages for exceptions when
    dealing with server responses"""

    def __init__(self, code, additional_info):
        """Constructor uses status code and info defined in each subclass"""
        self._response_code = code
        self._message = f"There was an error in the communication with the server. " \
                        f"The server response code was {self._response_code}.\n"
        self._message += additional_info

# String method defined to be used with representation method when displaying info to user
    def __str__(self):
        return self._message

    def __repr__(self):
        return str(type(self))


# Additional info around requests and recommendations sourced online
# Each error handled here is individual subclass of custom Exception class
class Error400(CommunicationError):

    def __init__(self):
        self._code = 400
        self.info = "Bad request error: there is an error in the request itself " \
                    "e.g. syntax errors. Check the request itself and try again."
        super().__init__(self._code, self.info)


class Error401(CommunicationError):

    def __init__(self):
        self._code = 401
        self._info = "Unauthorized error: This really means unauthenticated error. " \
                     "The client must 'authenticate itself' to get response. This usually " \
                     "means that the user needs to login to be authenticated."
        super().__init__(self._code, self._info)


class Error403(CommunicationError):

    def __init__(self):
        self._code = 403
        self._info = "Forbidden error: Caused by not having access rights to the " \
                     "given request. Check that there are no issues with the API key " \
                     "as trying to access something that you don't have the API key for " \
                     "will likely cause this error."
        super().__init__(self._code, self._info)


class Error404(CommunicationError):

    def __init__(self):
        self._code = 404
        self._info = "Not Found error: The server couldn't find what you were looking " \
                     "for. Either the URL wasn't recognised or, for an API, the " \
                     "endpoint is valid but the resource itself doesn't exist."
        super().__init__(self._code, self._info)


class Error408(CommunicationError):

    def __init__(self):
        self._code = 408
        self._info = "Timeout error: The server wants to shut down an unused connection."
        super().__init__(self._code, self._info)


class Error429(CommunicationError):

    def __init__(self):
        self._code = 429
        self._info = "Too Many Requests error: Too many requests have been sent in " \
                     "a given amount of time."
        super().__init__(self._code, self._info)


class Error500(CommunicationError):

    def __init__(self):
        self._code = 500
        self._info = "Internal Server error: The server has encountered a situation that " \
                     "it doesn't know how to handle."
        super().__init__(self._code, self._info)


class Error511(CommunicationError):

    def __init__(self):
        self._code = 511
        self._info = "Network Authentication error: The client needs to authenticate " \
                     "i.e. login, to gain network access."
        super().__init__(self._code, self._info)

class CommunicationError(Exception):
    """Custom error handling class to adapt messages for exceptions when
    dealing with server responses"""

    def __init__(self, code, additional_info):
        """Constructor uses status code and info defined in each subclass"""
        self._response_code = code
        self._message = f"There was an error in the communication with the server. " \
                        f"The server response code was {self._response_code}.\n"
        self._message += additional_info

# String method defined to be used with representation method when displaying info to user
    def __str__(self):
        return self._message

    def __repr__(self):
        return str(type(self))


# Additional info around requests and recommendations sourced online
# Each error handled here is individual subclass of custom Exception class
class Error400(CommunicationError):

    def __init__(self):
        self._code = 400
        self.info = "Bad request error: there is an error in the request itself " \
                    "e.g. syntax errors. Check the request itself and try again."
        super().__init__(self._code, self.info)


class Error401(CommunicationError):

    def __init__(self):
        self._code = 401
        self._info = "Unauthorized error: This really means unauthenticated error. " \
                     "The client must 'authenticate itself' to get response. This usually " \
                     "means that the user needs to login to be authenticated."
        super().__init__(self._code, self._info)


class Error403(CommunicationError):

    def __init__(self):
        self._code = 403
        self._info = "Forbidden error: Caused by not having access rights to the " \
                     "given request. Check that there are no issues with the API key " \
                     "as trying to access something that you don't have the API key for " \
                     "will likely cause this error."
        super().__init__(self._code, self._info)


class Error404(CommunicationError):

    def __init__(self):
        self._code = 404
        self._info = "Not Found error: The server couldn't find what you were looking " \
                     "for. Either the URL wasn't recognised or, for an API, the " \
                     "endpoint is valid but the resource itself doesn't exist."
        super().__init__(self._code, self._info)


class Error408(CommunicationError):

    def __init__(self):
        self._code = 408
        self._info = "Timeout error: The server wants to shut down an unused connection."
        super().__init__(self._code, self._info)


class Error429(CommunicationError):

    def __init__(self):
        self._code = 429
        self._info = "Too Many Requests error: Too many requests have been sent in " \
                     "a given amount of time."
        super().__init__(self._code, self._info)


class Error500(CommunicationError):

    def __init__(self):
        self._code = 500
        self._info = "Internal Server error: The server has encountered a situation that " \
                     "it doesn't know how to handle."
        super().__init__(self._code, self._info)


class Error511(CommunicationError):

    def __init__(self):
        self._code = 511
        self._info = "Network Authentication error: The client needs to authenticate " \
                     "i.e. login, to gain network access."
        super().__init__(self._code, self._info)
Custom error handling class to adapt messages for exceptions when     dealing with server responses Constructor uses status code and info defined in each subclass # String method defined to be used with representation method when displaying info to user # Additional info around requests and recommendations sourced online # Each error handled here is individual subclass of custom Exception class