Boto3,python以及如何处理错误

时间:2022-09-30 23:10:28

I just picked up python as my go-to scripting language and I am trying to figure how to do proper error handling with boto3.

我只是选择python作为我的首选脚本语言,我试图找出如何使用boto3进行正确的错误处理。

I am trying to create an IAM user:

我正在尝试创建一个IAM用户:

def create_user(username, iam_conn):
    try:
        user = iam_conn.create_user(UserName=username)
        return user
    except Exception as e:
        return e

When the call to create_user succeeds, i get a neat object that contains the http status code of the API call and the data of the newly created user.

当对create_user的调用成功时,我得到一个整洁的对象,其中包含API调用的http状态代码和新创建的用户的数据。

Example:

例:

{'ResponseMetadata': 
      {'HTTPStatusCode': 200, 
       'RequestId': 'omitted'
      },
 u'User': {u'Arn': 'arn:aws:iam::omitted:user/omitted',
           u'CreateDate': datetime.datetime(2015, 10, 11, 17, 13, 5, 882000, tzinfo=tzutc()),
           u'Path': '/',
           u'UserId': 'omitted',
           u'UserName': 'omitted'
          }
}

This works great. But when this fails (like if the user already exists), i just get an object of type botocore.exceptions.ClientError with only text to tell me what went wrong.

这很好用。但是当这个失败时(比如用户已经存在),我只得到一个类型为botocore.exceptions.ClientError的对象,只有文本告诉我出了什么问题。

Example: ClientError('An error occurred (EntityAlreadyExists) when calling the CreateUser operation: User with name omitted already exists.',)

示例:ClientError('调用CreateUser操作时发生错误(EntityAlreadyExists):名称已省略的用户已存在。',)

This (AFAIK) makes error handling very hard because i can't just switch on the resulting http status code (409 for user already exists according to the AWS API docs for IAM). This makes me think that i must be doing something the wrong way. The optimal way would be for boto3 to never throw exceptions, but juts always return an object that reflects how the API call went.

这个(AFAIK)非常难以处理错误,因为我不能只打开生成的http状态代码(根据IAM的AWS API文档,用户已经存在409)。这让我觉得我必须以错误的方式做事。最好的方法是boto3永远不会抛出异常,但juts总是返回一个反映API调用方式的对象。

Can anyone enlighten me on this issue or point me in the right direction?

任何人都可以在这个问题上启发我或指出我正确的方向吗?

Thanks a lot!

非常感谢!

4 个解决方案

#1


202  

Use the response contained within the exception. Here is an example:

使用异常中包含的响应。这是一个例子:

import boto3
from botocore.exceptions import ClientError

try:
    iam = boto3.client('iam')
    user = iam.create_user(UserName='fred')
    print "Created user: %s" % user
except ClientError as e:
    if e.response['Error']['Code'] == 'EntityAlreadyExists':
        print "User already exists"
    else:
        print "Unexpected error: %s" % e

The response dict in the exception will contain the following:

异常中的响应dict将包含以下内容:

  • ['Error']['Code'] e.g. 'EntityAlreadyExists' or 'ValidationError'
  • ['错误'] ['代码']例如'EntityAlreadyExists'或'ValidationError'
  • ['ResponseMetadata']['HTTPStatusCode'] e.g. 400
  • ['ResponseMetadata'] ['HTTPStatusCode'],例如400
  • ['ResponseMetadata']['RequestId'] e.g. 'd2b06652-88d7-11e5-99d0-812348583a35'
  • ['ResponseMetadata'] ['RequestId']例如'd2b06652-88d7-11e5-99d0-812348583a35'
  • ['Error']['Message'] e.g. "An error occurred (EntityAlreadyExists) ..."
  • ['错误'] ['消息']例如“发生错误(EntityAlreadyExists)......”
  • ['Error']['Type'] e.g. 'Sender'
  • ['错误'] ['类型']例如“发件人”

For more information see botocore error handling.

有关更多信息,请参阅botocore错误处理。

[Updated: 2018-03-07]

[更新日期:2018-03-07]

The AWS Python SDK has begun to expose service exceptions on clients (though not on resources) that you can explicitly catch, so it is now possible to write that code something like this:

AWS Python SDK已经开始在客户端(虽然不是资源上)公开可以显式捕获的服务异常,因此现在可以编写如下代码:

import boto3
from botocore.exceptions import ClientError, ParamValidationError

try:
    iam = boto3.client('iam')
    user = iam.create_user(UserName='fred')
    print "Created user: %s" % user
except iam.exceptions.EntityAlreadyExistsException:
    print "User already exists"
except ParamValidationError as e:
    print "Parameter validation error: %s" % e
except ClientError as e:
    print "Unexpected error: %s" % e

Unfortunately, there is currently no documentation for these exceptions.

不幸的是,目前没有关于这些例外的文档。

#2


1  

Just an update to the 'no exceptions on resources' problem as pointed to by @jarmod (do please feel free to update your answer if below seems applicable)

只是@jarmod指出的'资源无异常'问题的更新(如果下面的内容似乎适用,请随时更新您的答案)

I have tested the below code and it runs fine. It uses 'resources' for doing things, but catches the client.exceptions - although it 'looks' somewhat wrong... it tests good, the exception classes are showing and matching when looked into using debugger at exception time...

我测试了下面的代码,运行正常。它使用'资源'来处理事务,但是捕获了client.exceptions - 尽管它看起来有些错误......它测试良好,异常类在异常时使用调试器进行显示和匹配...

It may not be applicable to all resources and clients, but works for data folders (aka s3 buckets).

它可能不适用于所有资源和客户端,但适用于数据文件夹(也称为s3存储桶)。

lab_session = boto3.Session() 
c = lab_session.client('s3') #this client is only for exception catching

try:
    b = s3.Bucket(bucket)
    b.delete()
except c.exceptions.NoSuchBucket as e:
    #ignoring no such bucket exceptions
    logger.debug("Failed deleting bucket. Continuing. {}".format(e))
except Exception as e:
    #logging all the others as warning
    logger.warning("Failed deleting bucket. Continuing. {}".format(e))

Hope this helps...

希望这可以帮助...

#3


0  

Or a comparison on the class name e.g.

或者对类名进行比较,例如

except ClientError as e:
    if 'EntityAlreadyExistsException' == e.__class__.__name__:
        # handle specific error

Because they are dynamically created you can never import the class and catch it using real Python.

因为它们是动态创建的,所以你永远不能导入类并使用真正的Python捕获它。

#4


-2  

You need to do something when it fails to handle the issue. Right now you are returning the actual exception. For example, if its not a problem that the user exists already and you want to use it as a get_or_create function maybe you handle the issue by returning the existing user object.

当它无法处理问题时,您需要做一些事情。现在你要返回实际的异常。例如,如果它不是用户已存在的问题并且您想将其用作get_or_create函数,则可以通过返回现有用户对象来处理该问题。

try:
    user = iam_conn.create_user(UserName=username)
    return user
except botocore.exceptions.ClientError as e:

    #this exception could actually be other things other than exists, so you want to evaluate it further in your real code.
    if e.message.startswith(
        'enough of the exception message to identify it as the one you want')

        print('that user already exists.')
        user = iam_conn.get_user(UserName=username)
        return user

    elif e.message.some_other_condition:

         #something else
    else:
         #unhandled ClientError
         raise(e)
except SomeOtherExceptionTypeYouCareAbout as e:
    #handle it

# any unhandled exception will raise here at this point.
# if you want a general handler

except Exception as e:
    #handle it.

That said, maybe it is a problem for your app, in which case you want to want to put the exception handler around the code that called your create user function and let the calling function determine how to deal with it, for example, by asking the user to input another username, or whatever makes sense for your application.

也就是说,也许这对你的应用程序来说是一个问题,在这种情况下你想要将异常处理程序放在调用你的创建用户函数的代码周围,让调用函数决定如何处理它,例如,通过询问用户输入另一个用户名,或任何对您的应用程序有意义的用户名。

#1


202  

Use the response contained within the exception. Here is an example:

使用异常中包含的响应。这是一个例子:

import boto3
from botocore.exceptions import ClientError

try:
    iam = boto3.client('iam')
    user = iam.create_user(UserName='fred')
    print "Created user: %s" % user
except ClientError as e:
    if e.response['Error']['Code'] == 'EntityAlreadyExists':
        print "User already exists"
    else:
        print "Unexpected error: %s" % e

The response dict in the exception will contain the following:

异常中的响应dict将包含以下内容:

  • ['Error']['Code'] e.g. 'EntityAlreadyExists' or 'ValidationError'
  • ['错误'] ['代码']例如'EntityAlreadyExists'或'ValidationError'
  • ['ResponseMetadata']['HTTPStatusCode'] e.g. 400
  • ['ResponseMetadata'] ['HTTPStatusCode'],例如400
  • ['ResponseMetadata']['RequestId'] e.g. 'd2b06652-88d7-11e5-99d0-812348583a35'
  • ['ResponseMetadata'] ['RequestId']例如'd2b06652-88d7-11e5-99d0-812348583a35'
  • ['Error']['Message'] e.g. "An error occurred (EntityAlreadyExists) ..."
  • ['错误'] ['消息']例如“发生错误(EntityAlreadyExists)......”
  • ['Error']['Type'] e.g. 'Sender'
  • ['错误'] ['类型']例如“发件人”

For more information see botocore error handling.

有关更多信息,请参阅botocore错误处理。

[Updated: 2018-03-07]

[更新日期:2018-03-07]

The AWS Python SDK has begun to expose service exceptions on clients (though not on resources) that you can explicitly catch, so it is now possible to write that code something like this:

AWS Python SDK已经开始在客户端(虽然不是资源上)公开可以显式捕获的服务异常,因此现在可以编写如下代码:

import boto3
from botocore.exceptions import ClientError, ParamValidationError

try:
    iam = boto3.client('iam')
    user = iam.create_user(UserName='fred')
    print "Created user: %s" % user
except iam.exceptions.EntityAlreadyExistsException:
    print "User already exists"
except ParamValidationError as e:
    print "Parameter validation error: %s" % e
except ClientError as e:
    print "Unexpected error: %s" % e

Unfortunately, there is currently no documentation for these exceptions.

不幸的是,目前没有关于这些例外的文档。

#2


1  

Just an update to the 'no exceptions on resources' problem as pointed to by @jarmod (do please feel free to update your answer if below seems applicable)

只是@jarmod指出的'资源无异常'问题的更新(如果下面的内容似乎适用,请随时更新您的答案)

I have tested the below code and it runs fine. It uses 'resources' for doing things, but catches the client.exceptions - although it 'looks' somewhat wrong... it tests good, the exception classes are showing and matching when looked into using debugger at exception time...

我测试了下面的代码,运行正常。它使用'资源'来处理事务,但是捕获了client.exceptions - 尽管它看起来有些错误......它测试良好,异常类在异常时使用调试器进行显示和匹配...

It may not be applicable to all resources and clients, but works for data folders (aka s3 buckets).

它可能不适用于所有资源和客户端,但适用于数据文件夹(也称为s3存储桶)。

lab_session = boto3.Session() 
c = lab_session.client('s3') #this client is only for exception catching

try:
    b = s3.Bucket(bucket)
    b.delete()
except c.exceptions.NoSuchBucket as e:
    #ignoring no such bucket exceptions
    logger.debug("Failed deleting bucket. Continuing. {}".format(e))
except Exception as e:
    #logging all the others as warning
    logger.warning("Failed deleting bucket. Continuing. {}".format(e))

Hope this helps...

希望这可以帮助...

#3


0  

Or a comparison on the class name e.g.

或者对类名进行比较,例如

except ClientError as e:
    if 'EntityAlreadyExistsException' == e.__class__.__name__:
        # handle specific error

Because they are dynamically created you can never import the class and catch it using real Python.

因为它们是动态创建的,所以你永远不能导入类并使用真正的Python捕获它。

#4


-2  

You need to do something when it fails to handle the issue. Right now you are returning the actual exception. For example, if its not a problem that the user exists already and you want to use it as a get_or_create function maybe you handle the issue by returning the existing user object.

当它无法处理问题时,您需要做一些事情。现在你要返回实际的异常。例如,如果它不是用户已存在的问题并且您想将其用作get_or_create函数,则可以通过返回现有用户对象来处理该问题。

try:
    user = iam_conn.create_user(UserName=username)
    return user
except botocore.exceptions.ClientError as e:

    #this exception could actually be other things other than exists, so you want to evaluate it further in your real code.
    if e.message.startswith(
        'enough of the exception message to identify it as the one you want')

        print('that user already exists.')
        user = iam_conn.get_user(UserName=username)
        return user

    elif e.message.some_other_condition:

         #something else
    else:
         #unhandled ClientError
         raise(e)
except SomeOtherExceptionTypeYouCareAbout as e:
    #handle it

# any unhandled exception will raise here at this point.
# if you want a general handler

except Exception as e:
    #handle it.

That said, maybe it is a problem for your app, in which case you want to want to put the exception handler around the code that called your create user function and let the calling function determine how to deal with it, for example, by asking the user to input another username, or whatever makes sense for your application.

也就是说,也许这对你的应用程序来说是一个问题,在这种情况下你想要将异常处理程序放在调用你的创建用户函数的代码周围,让调用函数决定如何处理它,例如,通过询问用户输入另一个用户名,或任何对您的应用程序有意义的用户名。