St4rb0y
St4rb0y

Reputation: 309

Handle errors inside class, but raise in other script?

I'm looking for a sound and practical approach to storing different errors and/or exceptions (i.e. IOError, ValueError, <CustomError>, etc.) inside a class instance, but refraining from raise-ing them straight away. Instead I'd like to somehow communicate them to the script that import-ed and created the class instance in the first place, and raise the errors and exceptions there.

I'm not looking for a strategy on how to catch exceptions. For my custom exceptions, I use classes!

Is there a general approach for this?

Thanks.

Upvotes: 1

Views: 1487

Answers (1)

milanbalazs
milanbalazs

Reputation: 5329

You can define a "container/collector" class which contains your custom exceptions (Of course, without raising). This collector call in my example is the MyExceptions class.

You can define an unique exception as a class which is inherited from the related built-in exception. You can find the build-in exceptions and the exception structure (bottom of page) on this page: https://docs.python.org/2/library/exceptions.html

The basic syntax of an unique exception:

class MyException1(BaseException):
    """
    Exception is inherited from 'BaseException'.
    """
    def __str__(self):
        return "This is my Exception1. Inherited from 'BaseException'"

You can set the message of the exception with the __str__ or the __repr__ magic methods (I have used the __str__ in my example). It means, in the above example if you raise the MyException1 exception then the message in the traceback will be This is my Exception1. Inherited from 'BaseException'. You can read more about these magic methods on the following page: https://stackoverflow.com/a/2626364/11502612

my_exceptions.py:

class MyExceptions(object):
    """
    Collector class of Unique Exceptions
    """

    class MyException1(BaseException):
        """
        Exception is inherited from 'BaseException'.
        """
        def __str__(self):
            return "This is my Exception1. Inherited from 'BaseException'"

    class MyException2(Exception):
        """
        Exception is inherited from 'Exception'.
        """
        def __str__(self):
            return "This is my Exception2. Inherited from 'Exception'"

    class MyValueError(ValueError):
        """
        Exception is inherited from 'ValueError'.
        """
        def __str__(self):
            return "This is my MyValueError. Inherited from 'ValueError'"

    class AttributeError(BaseException):
        """
        Redefined 'AttributeError'. Inherited from 'ValueError'
        """
        def __str__(self):
            return "Redefined 'AttributeError'. Inherited from 'ValueError'"

As you can see above my exception collector class is the MyExceptions. This class contains other classes and actually these are the unique exceptions. I have written more type of exceptions. The unique exceptions are inherited from different build-in exceptions and the last one shows how you can re-defined a built-in exception (more-or-less).

Usage of these exceptions (test.py):

import my_exceptions


def raise_my_exception1():
    raise my_exceptions.MyExceptions.MyException1


def raise_my_exception2():
    raise my_exceptions.MyExceptions.MyException2


def raise_my_value_error():
    raise my_exceptions.MyExceptions.MyValueError


def raise_my_attribute_error():
    raise my_exceptions.MyExceptions.AttributeError


def raise_original_attribute_error():
    raise AttributeError("This is the original (built-in) AttributeError exception")


function_list = [
    raise_my_exception1,
    raise_my_exception2,
    raise_my_value_error,
    raise_my_attribute_error,
    raise_original_attribute_error,
]

for func in function_list:
    try:
        func()
    except BaseException as e:
        print(e)

As you can see in my above test.py file, I have imported the my_exceptions.py file as a Python module (import my_exceptions). The unique exceptions are raised in the functions. You can access the exceptions: <Module.CollectorClass.Exception>. The last raise_original_attribute_error function raises the built-in AttributeError with a custom message (It shows how you can separate your own AttributeError exception and the built-in AttributeError). The function_list list contains the references of the functions (With this solution I can call the function in a for loop and I don't need to call them one-by-one). In the for loop I defined a try/except structure and call the functions. I have used the BaseException built-in exception because it is the more wide exception in Python.

Output of the script:

>>> python2 test.py 
This is my Exception1. Inherited from 'BaseException'
This is my Exception2. Inherited from 'Exception'
This is my MyValueError. Inherited from 'ValueError'
Redefined 'AttributeError'. Inherited from 'ValueError'
This is the original (built-in) AttributeError exception

You can catch your own exceptions in a try/except (Separately from others):

import my_exceptions


def raise_my_exception1():
    raise my_exceptions.MyExceptions.MyValueError


def raise_other_exception():
    raise Exception("Unexpected exception")

for func in [raise_my_exception1, raise_other_exception]:
    try:
        func()
    except my_exceptions.MyExceptions.MyValueError as e:
        print(e)
        print("Handled exception. Do nothing!")
    except Exception as e:
        print(e)
        print("Not handled exception. Raise it!")
        raise e

Output:

>>> python2 test.py 
This is my MyValueError. Inherited from 'ValueError'
Handled exception. Do nothing!
Unexpected exception
Not handled exception. Raise it!
Traceback (most recent call last):
  File "test.py", line 20, in <module>
    raise e
Exception: Unexpected exception

And of course, you have many choice to use this own exception collector.

You can create an instance from the exception collector class:

import my_exceptions

exception_collector = my_exceptions.MyExceptions()


def raise_my_exception1():
    raise exception_collector.MyValueError

If you are a strict OOP developer, your can inherited your "functional" class from your exception class. In that case your can use the exceptions as "instance exceptions" (with self). Probably this way what you are looking for!

Example:

import my_exceptions


class FunctionalClass(my_exceptions.MyExceptions):
    def raise_my_exception1(self):
        raise self.MyValueError

    def raise_other_exception(self):
        raise Exception("Unexpected exception")


functional_instance = FunctionalClass()

for func in [functional_instance.raise_my_exception1, functional_instance.raise_other_exception]:
    try:
        func()
    except my_exceptions.MyExceptions.MyValueError as e:
        print(e)
        print("Handled exception. Do nothing!")
    except Exception as e:
        print(e)
        print("Not handled exception. Raise it!")
        raise e

Output:

>>> python2 test.py 
This is my MyValueError. Inherited from 'ValueError'
Handled exception. Do nothing!
Unexpected exception
Not handled exception. Raise it!
Traceback (most recent call last):
  File "test.py", line 23, in <module>
    raise e
Exception: Unexpected exception

Upvotes: 1

Related Questions