Python has many strong conventions but I'm unclear on how best to manage exceptions for my module. I know it's generally good practice to define a custom exception for your module. E.g.:
class MyError(Exception): pass
However, what I'm not clear on is how specific exception classes should get? Should each code path that results in a failure have its own exception class? Or is it better to use only a few? A hierarchy of exceptions where each "leaf" exception inherits from a slightly more generic parent exception would allow the module's users to decide how fine-grained their exception handling is but I can see how that could become convoluted to deal with.
I know some modules use error codes to distinguish between different errors that use the same class, which is used something like this:
try: mymodule.do_something() except mymodule.MyError as error: if error.code == mymodule.SPECIFIC_ERROR_CODE: handle_error() else: raise
But that feels wrong to me unless you're purposefully mimicking another library.
Is there an ideal way to handle this? Or is this question too dependent on the situation for a generic best practice?