1

Consider writing a small library in python that has this one simple method x which accepts an argument ac & returns the computed value 10/ac. now the catch here is ac cannot be 0. so how do i handle this case in my method.there are these ways that comes to my mind.

NOTE: i have looked into python exception handling but it just shows how to use try except not the specific problem that i am asking.

method 1

def x(ac):
    try:
        return (10/ac)
    except  ZeroDivisionError:
        raise ZeroDivisionError("ac cannot be zero")

The above code just uses a plain try except block to catch spcific exceptions and raises the exception to the calling code. so the calling code would look like this:

# some script...
try:
    x(0)
except ZeroDivisionError as e:
    log(e)

but in this case, i would have to know before hand what all possible exceptions that method x might raise.

Method 2:

def x(ac):
    if ac == 0:
        raise ValueError("ac cannot be zero") # or ZeroDivisionError??
    else:
        return (10/ac)

i guess this is semantically same as previous one except that to check some conditions(that we know might occur) using if's & raising exceptions based on that. It also suffers from the same problem of knowing beforehand what exceptions the method might throw.

method 3

The last method is not handling any exception in the library method rather leave it to the client code but this obviously doesn't makes sense as the client would never be able to know why exactly the error might occur while resort to something like

def x(ac):
    return (10/ac)


    try:
       x(20)
    except Exception as e:
       log(e)

now this method here was a simple method with just one opertaion but what if the method itself is doing something complicated like connecting to the database then fetching some results. something like :

def x(db):
    conn = db.connect('localhost')
    results = connec.fetch(user_id=2)
    return results

if something is unclear please let me know.

anekix
  • 2,393
  • 2
  • 30
  • 57
  • 1
    Why would method 3 ever apply? You can *document* what exceptions to expect. I would reject any `except Exception` in a code review where the developer can't empirically figure out what exceptions could be thrown, and decent unit testing can expose anything we missed. – Martijn Pieters Aug 16 '18 at 09:50
  • @MartijnPieters yes exactly thats why i said method 3 dosent makes sense as it would end up hiding some other exception but still i have seen code like this so i mention it here. – anekix Aug 16 '18 at 09:52
  • 1
    Most of all, **it depends**. Usually, API-specific exception classes are created to be thrown instead. See the [`requests` library API documentation](http://www.python-requests.org/en/master/api/#exceptions) for example. Note how the API is *documented what exceptions to expect*. – Martijn Pieters Aug 16 '18 at 09:52
  • @MartijnPieters thanks for the link.will go through it – anekix Aug 16 '18 at 09:53

1 Answers1

6

It depends. Exceptions like TypeError or ValueError are standard and usually indicate programming errors. There is usually no need to be explicit about these.

The rest, you document. As an API author it's your responsibility to make it clear what behaviour another developer can expect from your code. If ZeroDivisionError is a good signal to document, then add that to the docstring of the API. At that point there is no need to explicitly catch and raise it again either. Don't count on the users reading your code, give them good documentation instead.

For many APIs it makes sense to define your own exceptions. The implementation can catch generic exceptions, or the custom exceptions of 3rd-party APIs it uses to do the work, then raise an API-specific exception to signal to the caller something is wrong, letting them handle the issue.

There are legion examples of popular Python libraries with such exceptions. Some examples:

What these projects have in common is good documentation, that explicitly names the exceptions that anyone using the project should be aware of.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • so catching the inbuilt exceptions and reraising them doesn't makes sense right?if absolutely necessary i might indicate it in the documentation.is this correct? – anekix Aug 16 '18 at 10:10
  • 2
    @anekix correct. But take into account that the traceback of an exception carries a lot of information too; sometimes it may be easier for the caller to understand where the problem lies if you caught an exception and raised the same exception again, using `raise ... from None` to [clear the exception context](https://stackoverflow.com/questions/24752395/python-raise-from-usage/24752607#24752607). That way the developer using the API can avoid having to scan your API's part of the stack and focus on their call to the API. – Martijn Pieters Aug 16 '18 at 10:20
  • 1
    @anekix: for your specific example, the traceback won't actually change in length, the `raise ZeroDivisionError()` line only customised the exception message. But the `10/ac` expression shown in the traceback when you don't catch and re-raise tells the developer just as much that it was `ac` that was at fault there, catch and re-raise thus has little added value but you now have to content with 4 extra lines of code in your project with a new level of indentation. That's a small cost here, but that's still a cost you now carry for very little benefit. – Martijn Pieters Aug 16 '18 at 10:25