• Stars
    star
    144
  • Rank 254,017 (Top 6 %)
  • Language
    Python
  • License
    Apache License 2.0
  • Created over 4 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Making botocore.exceptions.ClientError easier to deal with

aws-error-utils

Making botocore.exceptions.ClientError easier to deal with

All AWS service exceptions are raised by boto3 as a botocore.exceptions.ClientError, with the contents of the exception indicating what kind of exception happened. This is not very pythonic, and the contents themselves are rather opaque, most being held in dicts rather than as properties. The functions in this package help dealing with that, to make your code less verbose and require less memorization of ClientError contents.

Installation

The package is on PyPI for pip-installing, but you can also just copy the aws_error_utils.py file into your project.

aws-error-utils requires Python 3.7 or higher.

Usage

If you've got code like this:

import boto3, botocore.exceptions

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except botocore.exceptions.ClientError as error:
    if error.response['Error']['Code'] == 'NoSuchBucket':
        print(error.response['Error']['Message'])
        # error handling
    else:
        raise

you can replace it with:

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except errors.NoSuchBucket as error:
# or
# except catch_aws_error('NoSuchBucket') as error:
    print(error.message)
    # error handling

If you have trouble remembering where all the contents in ClientError are, like these:

client_error.response['Error']['Code']
client_error.response['Error']['Message']
client_error.response['ResponseMetadata']['HTTPStatusCode']
client_error.operation_name

you can replace it with:

import boto3
from aws_error_utils import get_aws_error_info

err_info = get_aws_error_info(client_error)

err_info.code
err_info.message
err_info.http_status_code
err_info.operation_name

If you're using errors or catch_aws_error(), you can skip the get_aws_error_info() step, because the fields are set directly on the ClientError object:

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except errors.NoSuchBucket as error:
    error.code
    error.message
    error.http_status_code
    error.operation_name

Additionally, when you do need to use ClientError, it's imported by aws_error_utils so you can just use aws_error_utils.ClientError rather than botocore.exceptions.ClientError (the same is true for BotoCoreError, the base class of all non-ClientError exceptions).

errors

It's easiest to use the errors class if you don't have complex conditions to match. Using the error code as a field name in an except block will match that error code. Additionally, when you use this style, it sets the fields from AWSErrorInfo (see below) directly on the ClientError object. For example:

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except errors.NoSuchBucket as error:
    print(error.message)

    # error handling

You can include multiple error codes in an except statement, though this is slower than combining them with a single catch_aws_error() call.

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except (errors.NoSuchBucket, errors.NoSuchKey) as error:
    # note: catch_aws_error("NoSuchBucket", "NoSuchKey") is faster
    print(error.message)

    # error handling

You can catch all ClientErrors with errors.ALL.

You can only use this style for error codes that work as Python property names. For error codes like EC2's InvalidInstanceID.NotFound, you have to use catch_aws_error() (see below).

Unfortunately, you cannot get tab completion for error codes on the errors class, as a comprehensive list of error codes is not available as a Python package (botocore has a small number, but they are few and incomplete).

Note that the value of errors.NoSuchBucket is not an exception class representing the NoSuchBucket error, it is an alias for catch_aws_error('NoSuchBucket'). It can only be used in an except statement; it will raise RuntimeError otherwise. You also cannot instantiate the errors class.

catch_aws_error()

The function takes as input error code(s), and optionally operation name(s), to match against the current raised exception. If the exception matches, the except block is executed. If your error handling still needs the error object, you can still use an as expression, otherwise it can be omitted (just except catch_aws_error(...):). Additionally, catch_aws_error() sets the fields from AWSErrorInfo (see below) directly on the ClientError object.

import boto3
from aws_error_utils import catch_aws_error

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except catch_aws_error('NoSuchBucket') as error:
    print(error.message)

    # error handling

You can provide error codes either as positional args, or as the code keyword argument with either as a single string or a list of strings.

catch_aws_error('NoSuchBucket')
catch_aws_error(code='NoSuchBucket')

catch_aws_error('NoSuchBucket', 'NoSuchKey')
catch_aws_error(code=['NoSuchBucket', 'NoSuchKey'])

If there are multiple API calls in the try block, and you want to match against specific ones, the operation_name keyword argument can help. Similar to the code keyword argument, the operation name(s) can be provided as either as a single string or a list of strings.

import boto3
from aws_error_utils import catch_aws_error

try:
    s3 = boto3.client('s3')
    s3.list_objects_v2(Bucket='bucket-1')
    s3.get_object(Bucket='bucket-2', Key='example')
except catch_aws_error('NoSuchBucket', operation_name='GetObject') as error:
    # This will be executed if the GetObject operation raises NoSuchBucket
    # but not if the ListObjects operation raises it

You must provide an error code. To match exclusively against operation name, use the aws_error_utils.ALL_CODES token. For completeness, there is also an ALL_OPERATIONS token.

import boto3
from aws_error_utils import ALL_CODES, catch_aws_error

try:
    s3 = boto3.client('s3')
    s3.list_objects_v2(Bucket='bucket-1')
    s3.get_object(Bucket='bucket-1', Key='example')
except catch_aws_error(ALL_CODES, operation_name='ListObjectsV2') as e:
    # This will execute for all ClientError exceptions raised by the ListObjectsV2 call

For more complex conditions, instead of providing error codes and operation names, you can provide a callable to evaluate the exception. Note that unlike error codes, you can only provide a single callable.

import re
import boto3
from aws_error_utils import catch_aws_error, get_aws_error_info

def matcher(e):
    info = get_aws_error_info(e)
    return re.search('does not exist', info.message)

try:
    s3 = boto3.client('s3')
    s3.list_objects_v2(Bucket='bucket-1')
except catch_aws_error(matcher) as e:
    # This will be executed if e is a ClientError and matcher(e) returns True
    # Note the callable can assume the exception is a ClientError

get_aws_error_info()

This function takes a returns an AWSErrorInfo object, which is a dataclass with the following fields:

  • code
  • message
  • http_status_code
  • operation_name
  • response (the raw response dictionary)

If you're not modifying your except statements to use catch_aws_error(), this function can be useful instead of remembering exactly how this information is stored in the ClientError object.

If you're using catch_aws_error(), this function isn't necessary, because it sets these fields directly on the ClientError object.

aws_error_matches()

This is the matching logic behind catch_aws_error(). It takes a ClientError, with the rest of the arguments being error codes and operation names identical to catch_aws_error(), except that it does not support providing a callable.

make_aws_error()

For testing, you can create ClientErrors with the make_aws_error() function, which takes the following arguments:

  • code (required)
  • message (required)
  • operation_name (required)
  • http_status_code
  • response

If a response dict is provided, it will be copied rather than modified.

More Repositories

1

aws-sso-util

Smooth out the rough edges of AWS SSO (temporarily, until AWS makes it better).
Python
848
star
2

aws-assume-role-lib

Assumed role session chaining (with credential refreshing) for boto3
Python
139
star
3

aws-export-credentials

Get AWS credentials from a profile to inject into other programs
Python
132
star
4

aws-whoami

A tool to show what AWS account and identity you're using.
Python
90
star
5

sfn-callback-urls

An application for easy handling of Step Function tokens as callback URLs
Python
46
star
6

aws-sso-credential-process

Bring AWS SSO-based credentials to the AWS SDKs until they have proper support
Python
44
star
7

aws-whoami-golang

A tool to show what AWS account and identity you're using.
Go
32
star
8

heaviside

AWS Step Functions but (almost) all done inline in Lambda
Python
25
star
9

aws-lambda-api-event-utils

Lightweight processing of API events for AWS Lambda
Python
19
star
10

imds-credential-server

CLI tool for providing AWS credentials to a container from the host
Go
16
star
11

permissions-boundary-test

Demonstration that AWS IAM policy evaluation docs are incorrect
Python
15
star
12

github-actions-boto3-demo

Demonstrate how GitHub OIDC token getting should be included in boto3
Python
13
star
13

faas-form

A command line tool invoking self-describing Lambda functions.
Python
9
star
14

aws-arn

Create properly formatted AWS ARNs according to service rules
Python
9
star
15

aws-sso-login-gui

A prototype app for getting users logged in via AWS SSO without requiring the AWS CLI
Python
8
star
16

tab-decay

Chrome extension to get rid of old tabs, like you know you should
JavaScript
7
star
17

api-concierge-cli

Python
6
star
18

aws-sso-cfn-helper

Work around current capabilities of AWS SSO CloudFormation resources
Python
5
star
19

serverless-example-project

Python
4
star
20

aws-docs-diff

Python
3
star
21

monotonic-versioning-manifesto

Better versioning than SemVer
2
star
22

ignorelib

Use the syntax and semantics of gitignore with custom ignore file names and locations
Python
2
star
23

cfn-lbr-registry

Set up a stack of Lambda functions for Lambda-backed custom resources (LBRs)
Python
1
star
24

cfn-transform-library

CloudFormation template transformations
Python
1
star
25

homebrew-tap

1
star