# -*- coding: utf-8 -*-
"""
:Module: khoros.errors.exceptions
:Synopsis: Collection of exception classes relating to the khoros library
:Usage: ``import khoros.errors.exceptions``
:Example: ``raise khoros.errors.exceptions.BadCredentialsError()``
:Created By: Jeff Shurtliff
:Last Modified: Jeff Shurtliff
:Modified Date: 01 Nov 2022
"""
#################
# Base Exception
#################
# Define base exception class
[docs]
class KhorosError(Exception):
"""This is the base class for Khoros exceptions."""
pass
############################
# Base Structure Exceptions
############################
[docs]
class InvalidStructureTypeError(KhorosError):
"""This exception is used when an invalid node type is provided.
.. versionadded:: 2.1.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The structure type that was provided is invalid."
if not (args or kwargs):
args = (default_msg,)
elif 'val' in kwargs:
custom_msg = f"{default_msg.split('structure type ')[0]}'{kwargs['val']}'{default_msg.split('The')[1]}"
args = (custom_msg,)
super().__init__(*args)
############################
# Authentication Exceptions
############################
[docs]
class InvalidCallbackURLError(KhorosError):
"""This exception is used when an invalid Callback URL for OAuth 2.0 was not provided."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The OAuth 2.0 callback URL that was provided is invalid. The entire URL must be provided."
if not (args or kwargs):
args = (default_msg,)
elif 'val' in kwargs:
custom_msg = f"{default_msg.split('URL ')[0]}'{kwargs['val']}'{default_msg.split('URL')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class MissingAuthDataError(KhorosError):
"""This exception is used when authentication data is not supplied and therefore a connection cannot occur."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The authentication data was not provided and a connection cannot be established."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class SessionAuthenticationError(KhorosError):
"""This exception is used when the session key authentication attempt failed."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The session key authentication attempt failed."
custom_msg = default_msg.replace('.', ' with the following message:')
if not (args or kwargs):
args = (default_msg,)
elif 'message' in kwargs:
custom_msg = f"{custom_msg} {kwargs['message']}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class SsoAuthenticationError(KhorosError):
"""This exception is used when the SSO authentication attempt failed.
.. versionadded:: 4.2.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The SSO authentication attempt failed."
custom_msg = default_msg.replace('.', ' with the following message:')
if not (args or kwargs):
args = (default_msg,)
elif 'message' in kwargs:
custom_msg = f"{custom_msg} {kwargs['message']}"
args = (custom_msg,)
super().__init__(*args)
#####################
# General Exceptions
#####################
[docs]
class CurrentlyUnsupportedError(KhorosError):
"""This exception is used when a feature or functionality being used is currently unsupported.
.. versionchanged:: 4.5.0
Introduced the ability for a fully customized message to be displayed.
.. versionchanged:: 2.0.0
The unsupported feature can be passed as a string argument to explicitly reference it in the exception.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "This feature is currently unsupported at this time."
if not (args or kwargs):
args = (default_msg,)
elif 'message' in kwargs:
args =(kwargs['message'],)
else:
custom_msg = f"The '{args[0]}' {default_msg.split('This ')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class DataMismatchError(KhorosError):
"""This exception is used when there is a mismatch between two data sources.
.. versionchanged:: 5.1.2
Fixed an issue where the message would not display properly.
.. versionadded:: 2.3.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "A data mismatch was found with the data sources."
if not (args or kwargs):
args = (default_msg,)
elif 'data' in kwargs:
multi_types = [list, tuple, set]
if type(kwargs['data']) == str:
custom_msg = f"{default_msg.split('the data')[0]}the '{kwargs['data']}'{default_msg.split('with the')[1]}"
custom_msg = custom_msg.replace('sources', 'source')
args = (custom_msg,)
elif type(kwargs['data']) in multi_types and len(kwargs['data']) == 2:
custom_section = f"'{kwargs['data'][0]}' and '{kwargs['data'][1]}'"
custom_msg = f"{default_msg.split('data sources')[0]}{custom_section}{default_msg.split('with the')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class InvalidParameterError(KhorosError):
"""This exception is used when an invalid parameter is provided.
.. versionadded:: 5.2.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The parameter that was provided is invalid."
if not (args or kwargs):
args = (default_msg,)
elif 'val' in kwargs:
custom_msg = f"{default_msg.split('parameter ')[0]}'{kwargs['val']}'{default_msg.split('The')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class InvalidFieldError(KhorosError):
"""This exception is used when an invalid field is provided.
.. versionadded:: 2.1.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The field that was provided is invalid."
if not (args or kwargs):
args = (default_msg,)
elif 'val' in kwargs:
custom_msg = f"{default_msg.split('field ')[0]}'{kwargs['val']}'{default_msg.split('The')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class InvalidURLError(KhorosError):
"""This exception is used when a provided URL is invalid.
.. versionadded:: 2.1.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The provided URL is invalid"
if not (args or kwargs):
args = (default_msg,)
elif 'url' in kwargs:
custom_msg = f"{default_msg.split('is')[0]}'{kwargs['url']}'{default_msg.split('URL')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class MissingRequiredDataError(KhorosError):
"""This exception is used when a function or method is missing one or more required arguments.
.. versionchanged:: 4.0.0
The exception can now accept the ``param`` keyword argument.
.. versionadded:: 2.0.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "Missing one or more required parameters"
init_msg = "The object failed to initialize as it is missing one or more required arguments."
param_msg = "The required parameter 'PARAMETER_NAME' is not defined"
if not (args or kwargs):
args = (default_msg,)
elif 'init' in args or 'initialize' in args:
if 'object' in kwargs:
custom_msg = f"{init_msg.split('object')[0]}'{kwargs['object']}'{init_msg.split('The')[1]}"
args = (custom_msg,)
else:
args = (init_msg,)
elif 'param' in kwargs:
args = (param_msg.replace('PARAMETER_NAME', kwargs['param']),)
else:
args = (default_msg,)
super().__init__(*args)
[docs]
class UnknownFileTypeError(KhorosError):
"""This exception is used when a file type of a given file cannot be identified.
.. versionadded:: 2.2.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The file type of the given file path cannot be identified."
if not (args or kwargs):
args = (default_msg,)
elif 'file' in kwargs:
custom_msg = f"{default_msg.split('path')[0]}'{kwargs['file']}'{default_msg.split('path')[1]}"
args = (custom_msg,)
super().__init__(*args)
#########################
# Generic API Exceptions
#########################
[docs]
class APIConnectionError(KhorosError):
"""This exception is used when the API query could not be completed due to connection aborts and/or timeouts."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The API query could not be completed due to connection aborts and/or timeouts."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class APIRequestError(KhorosError):
"""This exception is used for generic API request errors when there isn't a more specific exception.
.. versionchanged:: 4.5.0
Fixed an issue with the default message.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The API request did not return a successful response."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class DELETERequestError(KhorosError):
"""This exception is used for generic DELETE request errors when there isn't a more specific exception."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The DELETE request did not return a successful response."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class GETRequestError(KhorosError):
"""This exception is used for generic GET request errors when there isn't a more specific exception.
.. versionchanged:: 3.2.0
Enabled the ability to optionally pass ``status_code`` and/or ``message`` arguments.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The GET request did not return a successful response."
custom_msg = "The GET request failed with the following message:"
if 'status_code' in kwargs or 'message' in kwargs:
if 'status_code' in kwargs:
status_code_msg = f"returned the {kwargs['status_code']} status code"
custom_msg = custom_msg.replace('failed', status_code_msg)
if 'message' in kwargs:
custom_msg = f"{custom_msg} {kwargs['message']}"
else:
custom_msg = custom_msg.split(' with the following')[0] + "."
args = (custom_msg,)
elif not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class InvalidEndpointError(KhorosError):
"""This exception is used when an invalid API endpoint / service is provided.
.. versionchanged:: 5.1.2
Removed part of the default message that was specifically for Khoros JX, which is obsolete.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The supplied endpoint for the API is not recognized."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class InvalidLookupTypeError(KhorosError):
"""This exception is used when an invalid API lookup type is provided."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The supplied lookup type for the API is not recognized. (Examples of valid " + \
"lookup types include 'id' and 'email')"
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class InvalidPayloadValueError(KhorosError):
"""This exception is used when an invalid value is provided for a payload field.
.. versionadded:: 2.6.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "An invalid payload value was provided."
custom_msg = "The invalid payload value 'X' was provided for the 'Y' field."
if not (args or kwargs):
args = (default_msg,)
elif 'value' in kwargs:
if 'field' in kwargs:
custom_msg = custom_msg.replace('X', kwargs['value']).replace('Y', kwargs['field'])
else:
custom_msg = f"{custom_msg.replace('X', kwargs['value']).split(' for the')[0]}."
args = (custom_msg,)
super().__init__(*args)
[docs]
class InvalidRequestTypeError(KhorosError):
"""This exception is used when an invalid API request type is provided."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The supplied request type for the API is not recognized. (Examples of valid " + \
"request types include 'POST' and 'PUT')"
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class LookupMismatchError(KhorosError):
"""This exception is used when an a lookup value doesn't match the supplied lookup type."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The supplied lookup type for the API does not match the value that was provided."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class NotFoundResponseError(KhorosError):
"""This exception is used when an API query returns a 404 response and there isn't a more specific class."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The API query returned a 404 response."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class PayloadMismatchError(KhorosError):
"""This exception is used when more than one payload is supplied for an API request.
.. versionadded:: 3.2.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "More than one payload was provided for the API call when only one is permitted."
if not (args or kwargs):
args = (default_msg,)
elif kwargs['request_type']:
custom_msg = default_msg.replace("API call", f"{kwargs['request_type'].upper()} request")
args = (custom_msg,)
super().__init__(*args)
[docs]
class POSTRequestError(KhorosError):
"""This exception is used for generic POST request errors when there isn't a more specific exception.
.. versionchanged:: 3.2.0
Enabled the ability to optionally pass ``status_code`` and/or ``message`` arguments.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The POST request did not return a successful response."
custom_msg = "The POST request failed with the following message:"
if 'status_code' in kwargs or 'message' in kwargs:
if 'status_code' in kwargs:
status_code_msg = f"returned the {kwargs['status_code']} status code"
custom_msg = custom_msg.replace('failed', status_code_msg)
if 'message' in kwargs:
custom_msg = f"{custom_msg} {kwargs['message']}"
else:
custom_msg = custom_msg.split(' with the following')[0] + "."
args = (custom_msg,)
elif not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class PUTRequestError(KhorosError):
"""This exception is used for generic PUT request errors when there isn't a more specific exception.
.. versionchanged:: 3.2.0
Enabled the ability to optionally pass ``status_code`` and/or ``message`` arguments.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The PUT request did not return a successful response."
custom_msg = "The PUT request failed with the following message:"
if 'status_code' in kwargs or 'message' in kwargs:
if 'status_code' in kwargs:
status_code_msg = f"returned the {kwargs['status_code']} status code"
custom_msg = custom_msg.replace('failed', status_code_msg)
if 'message' in kwargs:
custom_msg = f"{custom_msg} {kwargs['message']}"
else:
custom_msg = custom_msg.split(' with the following')[0] + "."
args = (custom_msg,)
elif not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
####################
# Helper Exceptions
####################
[docs]
class InvalidHelperFileTypeError(KhorosError, ValueError):
"""This exception is used when an invalid file type is provided for the helper file."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The helper configuration file can only have the 'yaml' or 'json' file type."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class InvalidHelperArgumentsError(KhorosError):
"""This exception is used when the helper function was supplied arguments instead of keyword arguments."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The helper configuration file only accepts basic keyword arguments. (e.g. arg_name='arg_value')"
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class HelperFunctionNotFoundError(KhorosError):
"""This exception is used when a function referenced in the helper config file does not exist."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The function referenced in the helper configuration file could not be found."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
##################
# LiQL Exceptions
##################
[docs]
class InvalidOperatorError(KhorosError):
"""This exception is used when an invalid operator is provided for the LiQL query."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "An invalid operator was provided for the LiQL query."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class LiQLParseError(KhorosError):
"""This exception is used when a function is unable to successfully parse a LiQL response.
.. versionadded:: 3.2.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "Failed to parse the LiQL query response."
if not (args or kwargs):
args = (default_msg,)
elif 'message' in kwargs and kwargs['message']:
custom_section = f" as the query failed with the following message: {kwargs['message']}"
custom_msg = default_msg.replace('.', custom_section)
args = (custom_msg,)
super().__init__(*args)
[docs]
class OperatorMismatchError(KhorosError):
"""This exception is used when the number of operators in the LiQL query does not match the number of fields."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The number of operators provided in the LiQL query does not match the number of fields/values."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
[docs]
class TooManyResultsError(KhorosError):
"""This exception is used when more results are returned than were expected in a LiQL query.
.. versionchanged:: 3.2.0
Fixed the default message to be appropriate as it was the same message found in another exception.
.. versionadded:: 2.0.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "More results were returned in the LiQL response than were expected."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
#####################
# Message Exceptions
#####################
[docs]
class MessageTypeNotFoundError(KhorosError):
"""This exception is used when a message type cannot be identified from a given URL.
.. versionchanged:: 5.1.2
Fixed an issue with the message displaying properly when a message type is explicitly defined.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The message type could not be identified in the provided URL."
if not (args or kwargs):
args = (default_msg,)
elif 'msg_type' in kwargs:
custom_msg = f"{default_msg.split('message type ')[0]}message type '{kwargs['msg_type']}'{default_msg.split('type')[1]}"
args = (custom_msg,)
elif 'url' in kwargs:
custom_msg = f"{default_msg.split('provided')[0]}following URL: {kwargs['url']}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class InvalidMessagePayloadError(KhorosError):
"""This exception is used when the payload for creating a message is invalid."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The message payload is invalid and cannot be utilized."
if not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
##################
# Node Exceptions
##################
[docs]
class InvalidNodeTypeError(KhorosError):
"""This exception is used when an invalid node type is provided."""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The node type that was provided is invalid."
if not (args or kwargs):
args = (default_msg,)
elif 'val' in kwargs:
custom_msg = f"{default_msg.split('node type ')[0]}'{kwargs['val']}'{default_msg.split('The')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class NodeIDNotFoundError(KhorosError):
"""This exception is used when a valid Node ID could not be found in a provided URL.
.. versionchanged:: 5.1.2
Fixed an issue with how the message is displayed when a value is passed as an argument.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "A valid Node ID could not be identified in the given URL."
if not (args or kwargs):
args = (default_msg,)
elif 'val' in kwargs:
custom_msg = f"{default_msg.split('.')[0]}: {kwargs['val']}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class NodeTypeNotFoundError(KhorosError):
"""This exception is used when a valid node type could not be found in a provided URL.
.. versionchanged:: 5.1.2
Fixed an issue with how the message is displayed when a value is passed as an argument.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "A valid node type could not be identified in the given URL."
if not (args or kwargs):
args = (default_msg,)
elif 'val' in kwargs:
custom_msg = f"{default_msg.split('.')[0]}: {kwargs['val']}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class UnsupportedNodeTypeError(KhorosError):
"""This exception is used when an unsupported node type has been provided.
.. versionadded:: 3.2.0
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The node type is unsupported with the given operation."
if 'node_type' in kwargs:
custom_msg = f"{default_msg.split('node ')[0]}'{kwargs['node_type']}' node{default_msg.split('node')[1]}"
if 'operation' in kwargs:
custom_msg = custom_msg.replace('the given operation', kwargs['operation'])
args = (custom_msg,)
elif not (args or kwargs):
args = (default_msg,)
super().__init__(*args)
##################
# Role Exceptions
##################
[docs]
class InvalidRoleError(KhorosError):
"""This exception is used when an invalid role is provided.
.. versionchanged:: 5.1.2
Fixed an issue with how the message is displayed when a value is passed as an argument.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The role is invalid"
if not (args or kwargs):
args = (default_msg,)
elif 'role' in kwargs:
custom_msg = f"{default_msg.split('role ')[0]}'{kwargs['role']}' role{default_msg.split('role')[1]}"
args = (custom_msg,)
super().__init__(*args)
[docs]
class InvalidRoleTypeError(KhorosError):
"""This exception is used when an invalid role type is provided.
.. versionchanged:: 5.1.2
Fixed an issue with how the message is displayed when a value is passed as an argument.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The role type is invalid"
if not (args or kwargs):
args = (default_msg,)
elif 'role_type' in kwargs:
custom_msg = f"{default_msg.split('type')[0]}type '{kwargs['role_type']}'{default_msg.split('type')[1]}"
args = (custom_msg,)
super().__init__(*args)
##################
# User Exceptions
##################
[docs]
class UserCreationError(KhorosError):
"""This exception is used when an attempt to create a user fails.
.. versionchanged:: 5.1.2
Fixed an issue with how the message is displayed when a value is passed as an argument.
"""
def __init__(self, *args, **kwargs):
"""This method defines the default or custom message for the exception."""
default_msg = "The user failed to be created."
if not (args or kwargs):
args = (default_msg,)
elif 'user' in kwargs:
custom_msg = f"{default_msg.split('user')[0]}user '{kwargs['user']}'{default_msg.split('user')[1]}"
args = (custom_msg,)
if 'exc_msg' in kwargs:
full_msg = f"{default_msg.split('.')[0]} due to the following exception: {kwargs['exc_msg']}"
args = (full_msg,)
super().__init__(*args)