[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Understanding exceptions [was: Python or Perl for a Debian maintainance project?]



Andrew Suffield <asuffield@debian.org> wrote:

> It was part of an argument against "Python is better because the code
> is shorter because [exceptions]" - which is only true when you don't
> handle the errors.

It seems you don't understand exceptions, so I will try to help you with
a real-world example (decoding of a network message; names were
anonymized because the original is closed source):

  try:
      if self.header["type"] == 231:
          pass
      elif self.header["type"] == 258:
          appli_str = sock.recv(24)
          (appli["field name 1"], appli["field name 2"], appli["field name 3"],
           appli["field name 4"], appli["field name 5"], appli["field name 6"],
           appli["field name 7"]) = \
               struct.unpack(">3s2s1s1s15s1s1s", appli_str)
      elif self.header["type"] == 259:
          appli_str = sock.recv(48)
          (appli["field name 2"], appli["field name 8"], appli["field name 9"],
           appli["field name 10"], appli["field name 4"]) = \
               struct.unpack(">2s2s6s6s31s1s", appli_str)
      elif self.header["type"] == 277:
          appli_str = sock.recv(8)
          (appli["field name 11"], appli["field name 12"],
           appli["field name 7"]) = struct.unpack(">4s1s3s", appli_str)
      elif self.header["type"] == 278:
          appli_str = sock.recv(4)
          (appli["field name 13"],) = struct.unpack(">4s", appli_str)
      else:
          raise FooProgInvalidReceivedHeaderTypeError(
              "a header of type %u was received by the FooMachine"
              % self.header["type"])
                
  except struct.error, val:
      raise FooProgBodyDecodingError(val)

So, you see, the code *is* made shorter and clearer by the use of
exceptions: there is no need to check the "error code" after every
struct.unpack call; there is only one place where it is handled to
generate a useful exception whose type indicates precisely what happened
and whose value allows the upper layer to generate a useful error
message. Same thing if the message type was invalid (except that in this
case, there was no need to check for the *same* error type several
times).

The block in the first clause of the try statement contains logic,
nothing more. No need to obfuscate the logic with error handling. No
need to make the error handling sloppy in order to unobfuscate the
logic.

All FooProg* exceptions will be derived classes of a generic
FooProgGenericException exception that can be caught in the main loop
with a simple try... except FooProgGenericException statement if they
were not handled by more specific code, and can easily lead to the
output of a precise error message because the exception object carries
all the relevant information.

You can argue that the "if... [elif...] else" stuff is not as elegant as
a case statement[1], but I really doubt you can argue that the use of
exceptions does not make this outrageously common code sample shorter
and clearer.


[1] This can be changed (for instance, with the use of a dictionary
    whose keys are the valid message types), this was deemed unnecessary
    due to the small number of possible values of the message type.

-- 
Florent



Reply to: