Python: Except block capturing exception that is not listed for block

Question:

I have a function that makes a request to AWS using boto3, and I wish to capture any exceptions and raise one of two custom errors, depending on the botocore.exceptions exception that is returned. I thought I had this working, but an except block is catching errors that are not listed for that block.

First and foremost, I want to return the custom AwsUnauthorizedException for any SSO errors. This is fairly simple, and works as follows.

aws_unauthorized_exceptions = (botocore.exceptions.SSOError, botocore.exceptions.SSOTokenLoadError, botocore.exceptions.UnauthorizedSSOTokenError)
...
except aws_unauthorized_exceptions as e:
    err_msg = str(e)
    details = {'aws_error_message': err_msg, **req_msg}
    raise AwsUnauthorizedException(**details) from None

However next I wish to return the custom AwsBadRequestException for any other botocore.exceptions exception. The trouble is, using Exception means that every other exception is caught, rather than just other botocore.exceptions exceptions, which isn’t what I want to do at this point in my code.

So I’m using the inspect module and creating a tuple containing all exceptions in botocore.exceptions, except for those in aws_unauthorized_exceptions.

for name, obj in inspect.getmembers(botocore.exceptions):
    if inspect.isclass(obj) and obj not in aws_unauthorized_exceptions:
        aws_bad_request_exceptions = aws_bad_request_exceptions + (obj,)
...
except aws_bad_request_exceptions as e:
    err_msg = e.response['Error']['Message']
    details = {'aws_error_message': err_msg}
    raise AwsBadRequestException(**details)

However, when I include both except blocks with the block catching aws_bad_request_exceptions first, it still catches exceptions from aws_unauthorized_exceptions.

To be sure that the exception in question (botocore.exceptions.SSOTokenLoadError in this example) was not in the tuple aws_bad_request_exceptions I added the following line to the except block.

printed(type(e) in aws_bad_request_exceptions)

This printed False.

Can anyone suggest why I’m seeing this unexpected behaviour?

Asked By: David Gard

||

Answers:

All exceptions in botocore have a couple common superclasses: botocore.exceptions.BotoCoreError and botocore.exceptions.ClientError.

Your aws_bad_request_exceptions tuple will end up including those superclasses, and since except clauses are matched in order, all BotoCore exceptions get matched.

Happily, exactly since botocore’s errors have these superclasses, you don’t need to construct that tuple at all; just catch the auth errors in a first except block, then every other boto error in another block (and other exceptions will not be caught).

import botocore.exceptions

try:
    ...
except (
    botocore.exceptions.SSOError,
    botocore.exceptions.SSOTokenLoadError,
    botocore.exceptions.UnauthorizedSSOTokenError,
) as auth_err:
    err_msg = str(auth_err)
    details = {'aws_error_message': err_msg, **req_msg}
    raise AwsUnauthorizedException(**details) from None
except (
    botocore.exceptions.BotoCoreError,
    botocore.exceptions.ClientError,
) as boto_err:
    ...
Answered By: AKX
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.