Boto3, python and how to handle errors

匿名 (未验证) 提交于 2019-12-03 02:44:02

问题:

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.

I am trying to create an IAM user:

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.

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.

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

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.

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

Thanks a lot!

回答1:

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:

  • ['Error']['Code'] e.g. 'EntityAlreadyExists' or 'ValidationError'
  • ['ResponseMetadata']['HTTPStatusCode'] e.g. 400
  • ['ResponseMetadata']['RequestId'] e.g. 'd2b06652-88d7-11e5-99d0-812348583a35'
  • ['Error']['Message'] e.g. "An error occurred (EntityAlreadyExists) ..."
  • ['Error']['Type'] e.g. 'Sender'

For more information see botocore error handling.

[Updated: 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:

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:

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.



回答3:

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)

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...

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

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...



回答4:

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.

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!