Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch specific exceptions with try...except

I have some code to rename a whole bunch of files and move them to a new directory using os.rename(). Its fairly simple, nothing flashy. It worked until I had some overlap in batches and there were duplicate files, this raised a WindowsError. Since the code worked in all otherways, I did

try:
    os.rename(...)
except WindowsError:
    print "Duplicate file {}".format(fileName)

This worked fine, except that it implies that all WindowsErrors are from duplicate files. The result was that when another aspect of my script broke, it failed essentially silently.

How can I employ try...except to catch only specific exceptions? If its not possible, what workarounds exist?

like image 337
wnnmaw Avatar asked Apr 30 '15 21:04

wnnmaw


2 Answers

According to the documentation:

"The errno value maps the winerror value to corresponding errno.h values."

Because of this, you should be able to distinguish between different Windows errors by using errno.

Example:

try:
    fp = open("nother")
except IOError as e:
    print e.errno
    print e
like image 168
mbomb007 Avatar answered Oct 11 '22 20:10

mbomb007


I wrote a context manager to suppress OSError based on errno value. Here's that context manager, just rewritten a little bit to catch WinError instead.

Your example code prints a message when the error occurs, but this won't print anything; it silently suppresses the exception.

I haven't tested this because I don't have Windows handy, but I tested the original and this is a trivial edit. Let me know if you have any trouble with this.

The .__exit__() method function gets information about any received exceptions, and if it returns True the exception is suppressed; if it returns False the exception is raised as normal. Thus this only suppresses the exception if the type matches (it was a WinError exception) and the .errno attribute matches the saved self.errno value.

class suppress_winerror(object):
    def __init__(self, errno):
        self.errno = errno

    def __enter__(self):
        return self

    def __exit__(self, e_type, e_val, e_tb):
        if e_type is not WinError:
            return False
        if e_val.errno != self.errno:
            return False
        return True

Example of how to use this. I'm assuming that the windows error code is being mapped onto errno.EEXIST, the errno code for "file already exists". Obviously if I got this wrong, you should put the correct error code instead.

import errno

with suppress_winerror(errno.EEXIST):
    os.rename(old_fname, new_fname)
like image 43
steveha Avatar answered Oct 11 '22 20:10

steveha