Home > database >  python exception handling with raise but why runtimeerror is needed
python exception handling with raise but why runtimeerror is needed

Time:02-18

I am trying to understand this piece of code from a Python book.

def client(hostname, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.connect((hostname, port))
    print('Client socket name is {}'.format(sock.getsockname()))

    delay = 0.1  # seconds
    text = 'This is another message'
    data = text.encode('ascii')
    while True:
        sock.send(data)
        print('Waiting up to {} seconds for a reply'.format(delay))
        sock.settimeout(delay)
        try:
            data = sock.recv(MAX_BYTES)
        except socket.timeout as exc:
            delay *= 2  # wait even longer for the next request
            if delay > 2.0:
                raise RuntimeError('I think the server is down') from exc
        else:
            break   # we are done, and can stop looping

    print('The server says {!r}'.format(data.decode('ascii')))

In this line:

raise RuntimeError('I think the server is down') from exc

I know "raise" can raise an exception and "exc" includes the exception. But I don't understand why we need "RuntimeError('I think the server is down')". What is the point of having this line our code as opposed to just having a "raise" statement only.

CodePudding user response:

In short:

The output of the two are different:

raise

Gives this output:

RuntimeError: No active exception to reraise #or whatever exception is active

Whereas:

raise RuntimeError('I think the server is down')

Gives:

RuntimeError: I think the server is down

In the latter you are specifying a runtime error where as the first you are just running the error from the exception.

Additional example:

For further explanation consider the following example:

try:
    9/0
except ZeroDivisionError as e:
    raise

Gives the output:

ZeroDivisionError: division by zero

Whereas:

try:
    9/0
except ZeroDivisionError as e:
    raise RuntimeError('I think the server is down') from e

This code give the output:

RuntimeError: I think the server is down

So this is a way to avoid one error and instead raise a different error.

Why this may be desired:

Imagine you have a program that searches for texts files and divides some number by the number of text files that it finds. If it does not find a file it will try: some_number/num_files which is some_number/0 and it will give ZeroDivisionError. In this case you may want to instead raise FileNotFoundError. In which case you could use: raise FileNotFoundError('There are no text files in the specified folder.').

CodePudding user response:

socket.timeout is a subclass of OSError. For whatever reason, the author of this code wants client to raise a RuntimeError instead in this case.

CodePudding user response:

This is a common practice. A package may handle a bunch of different types of errors deep in the bowels of its code. But how relevant are the execptions to the user of the module - especially when the package itself may change over time? It makes sense to aggregate these exceptions into a smaller, documented set of exceptions. Instead of a user needing to catch and figure out how to process dozens of exceptions whose meaning isn't very clear, the package can do the grunt work and pass back a more friendly set of errors.

Suppose this package had several different ways of getting this data from the server, or there were several different ways a downed server can be detected. The user doesn't care if its a timeout in this bit of code or perhaps it was detected in some other way. One could imagine middleware that is handing out server addresses realizes there is none available. The "bug" in this code is that the detailed error is not logged so there is no good way for anyone to figure out what happened.

This is all just speculation of course. I presume that in context of the book you are reading it makes sense. This is demo code showing a common practice of aggregating exceptions.

  • Related