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.