Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flatten nested try/except clauses

I want to try a number of different approaches at the same task, catching exceptions each time an approach fails. I know the exception which will be raised if an attempt fails (and it can be different for each attempt). After the last attempt, I want to give up gracefully and move on.

I currently do this through nested try/except clauses:

try:
    first_approach()
except Exception1:
    try:
        second_approach()
    except Exception2:
        try:
            third_approach()
        except:
            give_up()
except Exception2:
    try:
        third_approach()
    except:
        give_up()

But this looks bad to me because third_approach() is repeated. I can't see any help for this in the Python docs. So how can I flatten this ugly nested code?

A concrete example

For example, imagine I am attempting to read a list of CSV files, without knowing their encoding in advance.

Some of the CSV files may even be XLS files posing as CSVs via their file extension.

I therefore want to try a few different encodings then, if none of those encodings work, try to read the file as excel.

for f in files:
    try:
        read_csv(f, encoding='utf-8')
    except UnicodeDecodeError:
        try:
            read_csv(f, encoding='latin1')
        except NotCsvError:
            try:
                read_excel(f)
            except:
                give_up()
    except NotCsvError:
        try:
            read_excel(f)
        except:
            give_up()
like image 610
LondonRob Avatar asked Dec 24 '22 15:12

LondonRob


1 Answers

You could loop over your approach-functions with a for/else loop.

The else clause runs only if the for is not terminated by the break statement.

approaches = ((first_approach, [arg1, arg2, ...], {'kwarg1':kwarg1, 'kwarg2':kwarg2, ...}, (Approach1Exception1, Approach1Exception2, ...)),
              (second_approach, ..., ..., ...),
              (third_approach, ..., ..., ...), 
              ...)

for approach, args, kwargs, exceptions in approaches:
    try:
        approach(*args, **kwargs)
        break
    except exceptions: 
        pass
else:
    give_up()
like image 179
timgeb Avatar answered Jan 08 '23 03:01

timgeb