try except inside a with open

Cameron Simpson cs at cskk.id.au
Sat Jul 21 06:18:11 EDT 2018


Please try to preserve the attribution lines of previous authors. The inner 
quote below comes from Steven D'Aprano.

On 21Jul2018 15:13, Ganesh Pal <ganesh1pal at gmail.com> wrote:
>> (1) Since this function always returns True (if it returns at all), what
>> is the point? There's no point checking the return result, since it's
>> always true, so why bother returning anything?
>
>If  I don't return anything from a function it returns None.   But would it
>be better if for the  function  i.e modify_various_line(f) to  atleast catch
>or handle exceptions and log it for debugging purpose

Usually not.

Problems with this approach (catches and logging, and _concealing_ the 
exception from the caller) include:

1: the primary one is: if you do this, the caller will never know that anything 
went wrong

2: there isn't necessarily any logging setup anyway - small function should not 
need to know or care, otherwise they're hard to test and may make a lot of 
unwanted noise

Most functions do not know the larger programme context - it is therefore not 
possible to decide how to handle all exceptions. So unless the exception is 
anticipated for some specific situation, it is better to let it excape for the 
caller to handle (or not).

Look at your example function:

>Example:
>
>def modify_various_line(f):
>    """ Try modifiying various line """
>    try:
>        f.write('0123456789abcdef')
>        f.seek(5)     # Go to the 6th byte in the file
>        print f.read(1)
>        f.seek(-3, 2) # Go to the 3rd byte before the end
>        print f.read(1)
>        f.write('END')
>    except IOError as e:
>       logging.error("Error: got %s" , % (str(e)))

This function returns None regardless of success or failure. Generally for a 
function like this, which doesn't return a "result", you want it to return on 
success and raise an exception on failure. In that way, the caller can presume 
things are good if no exception occurred.

Here you print an error message to the log and return None anyway.

Firstly, your error message has no context: there's just an IOError in the log, 
so nobody can review the log and know what the error is associate with. If the 
IOError had been allowed to get out, the caller could have responded 
appropriately (or not, letting the exception further out to someone who can, or 
aborting the programme if nobody deals with it). This isn't as bad as it 
sounds; that abort is a symptom of a genuinely unanticipated error, which 
should be either understood properly and handled, or the invoker of the program 
should respond to its termination. In a sense, your whole programme is itself a 
function - if it didn't work, the person invoking it needs to find out.

Secondly, because you catch the error, the caller has no way to know the 
function did not accomplish what the caller expected. That is "silent failure",
and generally a bad thing.

Consider this trite function:

  def get_colour(thing, default='black'):
    try:
      colour = thing.colour
    except AttributeError:
      colour = default
    return colour

Here we handle the case where a thing has no .colour attribute and return a 
default. The function is designed for this, and has scope for the caller to 
specify the default.

The general rule in Python is: only catch an exception if you know exactly what 
to do with it, and your function will complete with a _correct_ result if you 
do it.

Conversely, _don't_ try to handle any other situation. (This makes your task 
easier, too!)

Here's an example:

  import errno
  ...
  # clean up a file...
  try:
    os.remove(filename)
  except OSError as e:
    if e.errno == errno.ENOENT:
      # it is ok if the file is already gone
      pass
    else:
      raise

Here we're catching all OSErrors, but only handling the specific situation we 
understand (the file doesn't exist, and in this case that is possible and 
normal). If there was any other kind of OSError (eg a permission error or 
anything else really - it is all unknown), we reraise the original exception.

There are any number of reasons that things can go wrong, including the code 
_calling_ your function asking for the wrong thing. Letting the exception out 
lets these situations get debugged.

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Python-list mailing list