Milano
Milano

Reputation: 18745

Try-Except-Else-Finally return in else alternative?

I'm working on a function which can raise multiple Exceptions. I want to handle these Exceptions in their except blocks and then return result with custom message and traceback. The problem is that finally is guaranteed so I can't return anything inside else block.

This code works if some Exception is raised but it doesn't work if there is no Exception. In such case, I want just return {'success':True}.

So from this code:

def foo():
    try:
        #some code
        return {'success':True}
    except FirstException:
        tb = traceback.format_exc()
        msg = 'There was FirstExc'
        return {'success':False,
                'tb':tb,
                'msg':msg}
    except SecondException:
        tb = traceback.format_exc()
        msg = 'There was SecondExc'
        return {'success':False,
                'tb':tb,
                'msg':msg}
    ...

I want not to repeat returns. I tried:

def foo():
    try:
        pass
    except FirstException:
        tb = traceback.format_exc()
        msg = 'There was FirstExc'
    except SecondException:
        tb = traceback.format_exc()
        msg = 'There was SecondExc'
    else:
        return {'success':True}
    finally:
        return {'success':False,
                'tb':tb,
                'msg':msg}

Do you know how to do it? I know I could put return {'success':True} into try block, remove finally and else blocks and add return {'success':False,'tb':tb,'msg':msg} into each except block but there are many except blocks so the code would be repeated multiple times.

Is there another option?

Upvotes: 1

Views: 861

Answers (2)

smac89
smac89

Reputation: 43196

def foo():
    success = False
    try:
        pass
    except FirstException:
        tb = traceback.format_exc()
        msg = 'There was FirstExc'
    except SecondException:
        tb = traceback.format_exc()
        msg = 'There was SecondExc'
    else:
        success = True
    finally:
        return {'success':success,
                'tb':tb,
                'msg':msg} if not success else {'success':success}

Alternative:

def foo():
    result = {"success": False}
    try:
        pass
    except FirstException:
        result['tb'] = traceback.format_exc()
        result['msg'] = 'There was FirstExc'
    except SecondException:
        result['tb'] = traceback.format_exc()
        result['msg'] = 'There was SecondExc'
    else:
        result['success'] = True
    finally:
        return result

Slight variation of the above:

def foo():
    result = {"success": False}
    try:
        pass
    except FirstException:
        result.update({'tb': traceback.format_exc(), 'msg': 'There was FirstExc'})
    except SecondException:
        result.update({'tb': traceback.format_exc(), 'msg': 'There was SecondExc'})
    else:
        result['success'] = True
    finally:
        return result

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1123440

Don't use a finally block. You want to return False for exceptions only, True otherwise; returning in a finally always applies for both cases.

You could either return from both except suites, or combine the suites.

Returning from both leads to more repetition:

def foo():
    try:
        pass
    except FirstException:
        tb = traceback.format_exc()
        msg = 'There was FirstExc'
        return {'success':False,
                'tb':tb,
                'msg':msg}
    except SecondException:
        tb = traceback.format_exc()
        msg = 'There was SecondExc'
        return {'success':False,
                'tb':tb,
                'msg':msg}
    else:
        return {'success':True}

You can combine the except suites into one:

def foo():
    try:
        pass
    except (FirstException, SecondException) as e:
        tb = traceback.format_exc()
        exception_type = 'FirstExc' if isinstance(e, FirstException) else 'SecondExc'
        msg = 'There was {}'.format(exception_type)
        return {'success':False,
                'tb':tb,
                'msg':msg}
    else:
        return {'success':True}

Another option is to build the return value first, then add information as needed:

def foo():
    result = {'success': True}
    try:
        pass
    except FirstException:
        tb = traceback.format_exc()
        msg = 'There was FirstExc'
        result = {'success': False, 'tb': tb, 'msg': msg}
    except SecondException:
        tb = traceback.format_exc()
        msg = 'There was SecondExc'
        result = {'success': False, 'tb': tb, 'msg': msg}
    finally:
        return result

This isn't really all that different from the return-from-the-except-suite option however.

Upvotes: 3

Related Questions