1

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 '19 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. – user317612 Feb 14 '19 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 '19 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 '19 at 23:33
  • @kevin, this is indeed a very good point. Persistence is a black box. – user317612 Feb 14 '19 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. – user317612 Feb 14 '19 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? – user317612 Feb 14 '19 at 2:58

Your Answer

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