0

Is it considered to be a good practice to convert all types of exceptions (exceptions from internal logic of application + exceptions from application's external dependencies - for example: File System) to application specific exceptions?

For example,

I am developing a Job Scheduling Software in Python (using layered architecture). One of the layer in this architecture is Persistence layer. This layer is responsible for storing/retrieving state of the Job to/from the persistence store (File System).

I have defined two application specific exception classes "PersistenceReadError" and "PersistenceWriteError" for exceptions raised from persistence layer APIs (read_jobs, write_jobs etc).

I am not sure if this is considered to be a good practice i.e. is it right to even catch exceptions like FileNotFoundError, FilePermissionsError etc. and wrap them in PersistenceRead/PersistenceWrite exceptions? Also how far should I go with creating exception classes vs using limited exception classes (to group similar exceptions together) with error code/messages to distinguish subtypes of exceptions.

  • I have defined two application specific exception classes " to what end? – Laiv Feb 13 at 21:41
  • @Laiv Sorry it was PersistenceRead and PersistenceWrite, I have update the question. These two exceptions are raised from the lowest layer in the application - i.e. the Persistence. I am thinking why all layers above persistence should handle low level File specific exceptions (maybe I would change persistence store from FS to DB in future) hence this wrapping. – Lokesh Agrawal Feb 14 at 2:50
3

It depends on what your overall approach and intent are.
I tend to keep the standard exceptions separate from the application-specific exceptions.
The question is, what do you do with them afterwards.

  1. Reraise as is: no need to wrap them in app-specific exceptions.
  2. Use in reporting or logging: wrapping is recommended.
  3. Add more info, such as error codes and messages: definitely wrap them.
  4. Group: if multiple exceptions are considered part of the same action/task.
    ie: FileNotFoundError, FilePermissionsError could raise PersistenceIOError

Example:

try:
    try:
        doSomething()
    except SystemError:
        do1()
        raise MainEx
    except ValueError:
        do2()
        raise MainEx
    except IOError as e:
        # Add more info
        raise PersistenceIOError(code=123, msg='File Bad', other=e)
    except TypeError:
        # Raise as is
        raise
    except Exception as e:
        raise UnknownEx
except MainEx:
    doMain()

Update:

Here's an idea of how I handle same exception with multiple error codes:

try:
    if a:
        raise PersistenceError(code=1, msg='1 Bad')
    elif b:
        raise PersistenceError(code=2, msg='2 Bad')
    else:
        raise PersistenceError(code=3, msg='3 Bad')
except PersistenceError as e:
    if e.code == 1:
        do1()
    elif e.code == 2:
        do2()
    elif e.code == 3:
        do3()
    else:
        raise
  • I would add that if you are going to build logic around these errors, wrapping is likely a good idea. – JimmyJames Feb 13 at 20:24
  • 1
    I'd also suggest focusing on the semantics of the wrappers and the functions which will be raising them. For example, if your persistence layer is a black box that "magically" persists data without the caller having to know where it lives, then allowing a FileNotFoundError to propagate out of the layer is probably a Bad Idea, because your caller will not know anything about the offending filesystem path. That should be transformed into a wrapper. But OTOH if you're "just" persisting to a path the caller gave you, then you should allow such errors to propagate as-is. – Kevin Feb 13 at 23:33
  • @kevin, this is indeed a very good point. Persistence is a black box. – Lokesh Agrawal Feb 14 at 2:39
  • My main difficulty with wrapping - how far should I go with creating exception classes vs using limited exception classes with error code to distinguish subtypes of exceptions. – Lokesh Agrawal Feb 14 at 2:44
  • @slybloty Thanks for the details. I am not sure how to handle grouped exceptions in the callee side of the function. For example, should I use errorcodes or use error message or use both to identify which error was thrown and whether I can handle it or not? – Lokesh Agrawal Feb 14 at 2:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.