web-dev-qa-db-fra.com

Boto3, python et comment gérer les erreurs

Je viens de choisir python comme langage de script privilégié et j'essaie de comprendre comment gérer correctement les erreurs avec boto3.

J'essaie de créer un utilisateur IAM:

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

Lorsque l'appel à create_user réussit, j'obtiens un objet soigné contenant le code de statut http de l'appel API et les données du nouvel utilisateur créé.

Exemple:

{'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'
          }
}

Cela fonctionne très bien. Mais lorsque cela échoue (comme si l'utilisateur existe déjà), je viens d'obtenir un objet de type botocore.exceptions.ClientError avec uniquement du texte pour me dire ce qui s'est mal passé.

Exemple: ClientError ('Une erreur s'est produite (EntityAlreadyExists) lors de l'appel de l'opération CreateUser: l'utilisateur dont le nom est omis existe déjà.',)

Cela (AFAIK) rend la gestion des erreurs très difficile car je ne peux pas simplement activer le code d’état http résultant (409 pour l’utilisateur existe déjà conformément à la documentation de l’API AWS pour IAM). Cela me fait penser que je dois faire quelque chose dans le mauvais sens. Le moyen optimal serait que boto3 ne lève jamais d'exception, mais les surpoids renvoient toujours un objet qui reflète le déroulement de l'appel de l'API.

Quelqu'un peut-il m'éclairer sur cette question ou me diriger dans la bonne direction?

Merci beaucoup!

156
SQDK

Utilisez la réponse contenue dans l'exception. Voici un exemple:

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)

La réponse dict dans l'exception contiendra les éléments suivants:

  • ['Error']['Code'] par exemple 'EntityAlreadyExists' ou 'ValidationError'
  • ['ResponseMetadata']['HTTPStatusCode'] par exemple 400
  • ['ResponseMetadata']['RequestId'] par exemple 'd2b06652-88d7-11e5-99d0-812348583a35'
  • ['Error']['Message'] par exemple "Une erreur est survenue (EntityAlreadyExists) ..."
  • ['Error']['Type'] par exemple 'Expéditeur'

Pour plus d'informations, voir traitement des erreurs botocore .

[Mise à jour: 2018-03-07]

Le kit SDK AWS Python _ a commencé à exposer les exceptions de service sur clients (mais pas sur ressources ) que vous pouvez explicitement intercepter, il est donc maintenant possible de écrivez ce code comme ceci:

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)

Malheureusement, il n'y a actuellement aucune documentation pour ces exceptions.

334
jarmod

Juste une mise à jour du problème "aucune exception sur les ressources" comme l'a souligné @jarmod (n'hésitez pas à mettre à jour votre réponse si le texte ci-dessous semble applicable)

J'ai testé le code ci-dessous et il fonctionne bien. Il utilise 'ressources' pour faire, mais intercepte le client.exceptions - bien qu'il 'semble' quelque peu faux ... il teste bien, les classes d'exception sont affichées et correspondent quand on les examine avec débogueur au moment de l'exception ...

Cela peut ne pas s'appliquer à toutes les ressources et à tous les clients, mais fonctionne pour les dossiers de données (ou seaux 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))

J'espère que cela t'aides...

12
R. Simac

Je l'ai trouvé très utile, car les exceptions ne sont pas documentées pour répertorier toutes les exceptions à l'écran de ce paquet. Voici le code que j'ai utilisé pour le faire:

import botocore.exceptions
def listexns(mod):
    #module = __import__(mod)
    exns = []
    for name in botocore.exceptions.__dict__:
        if (isinstance(botocore.exceptions.__dict__[name], Exception) or
            name.endswith('Error')):
            exns.append(name)
    for name in exns:
        print('%s.%s is an exception type' % (str(mod), name))
    return

if __== '__main__':
    import sys
    if len(sys.argv) <= 1:
        print('Give me a module name on the $PYTHONPATH!')
    print('Looking for exception types in module: %s' % sys.argv[1])
    listexns(sys.argv[1])

Ce qui résulte en:

Looking for exception types in module: boto3
boto3.BotoCoreError is an exception type
boto3.DataNotFoundError is an exception type
boto3.UnknownServiceError is an exception type
boto3.ApiVersionNotFoundError is an exception type
boto3.HTTPClientError is an exception type
boto3.ConnectionError is an exception type
boto3.EndpointConnectionError is an exception type
boto3.SSLError is an exception type
boto3.ConnectionClosedError is an exception type
boto3.ReadTimeoutError is an exception type
boto3.ConnectTimeoutError is an exception type
boto3.ProxyConnectionError is an exception type
boto3.NoCredentialsError is an exception type
boto3.PartialCredentialsError is an exception type
boto3.CredentialRetrievalError is an exception type
boto3.UnknownSignatureVersionError is an exception type
boto3.ServiceNotInRegionError is an exception type
boto3.BaseEndpointResolverError is an exception type
boto3.NoRegionError is an exception type
boto3.UnknownEndpointError is an exception type
boto3.ConfigParseError is an exception type
boto3.MissingParametersError is an exception type
boto3.ValidationError is an exception type
boto3.ParamValidationError is an exception type
boto3.UnknownKeyError is an exception type
boto3.RangeError is an exception type
boto3.UnknownParameterError is an exception type
boto3.AliasConflictParameterError is an exception type
boto3.PaginationError is an exception type
boto3.OperationNotPageableError is an exception type
boto3.ChecksumError is an exception type
boto3.UnseekableStreamError is an exception type
boto3.WaiterError is an exception type
boto3.IncompleteReadError is an exception type
boto3.InvalidExpressionError is an exception type
boto3.UnknownCredentialError is an exception type
boto3.WaiterConfigError is an exception type
boto3.UnknownClientMethodError is an exception type
boto3.UnsupportedSignatureVersionError is an exception type
boto3.ClientError is an exception type
boto3.EventStreamError is an exception type
boto3.InvalidDNSNameError is an exception type
boto3.InvalidS3AddressingStyleError is an exception type
boto3.InvalidRetryConfigurationError is an exception type
boto3.InvalidMaxRetryAttemptsError is an exception type
boto3.StubResponseError is an exception type
boto3.StubAssertionError is an exception type
boto3.UnStubbedResponseError is an exception type
boto3.InvalidConfigError is an exception type
boto3.InfiniteLoopConfigError is an exception type
boto3.RefreshWithMFAUnsupportedError is an exception type
boto3.MD5UnavailableError is an exception type
boto3.MetadataRetrievalError is an exception type
boto3.UndefinedModelAttributeError is an exception type
boto3.MissingServiceIdError is an exception type
7
jamescampbell

Ou une comparaison sur le nom de la classe, par exemple.

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

Comme ils sont créés dynamiquement, vous ne pouvez jamais importer la classe et l'attraper à l'aide de Python réel.

2
jmoz

Si vous appelez l'API sign_up (AWS Cognito) à l'aide de Python3, vous pouvez utiliser le code suivant.

def registerUser(userObj):
    ''' Registers the user to AWS Cognito.
    '''

    # Mobile number is not a mandatory field. 
    if(len(userObj['user_mob_no']) == 0):
        mobilenumber = ''
    else:
        mobilenumber = userObj['user_country_code']+userObj['user_mob_no']

    secretKey = bytes(settings.SOCIAL_AUTH_COGNITO_SECRET, 'latin-1')
    clientId = settings.SOCIAL_AUTH_COGNITO_KEY 

    digest = hmac.new(secretKey,
                msg=(userObj['user_name'] + clientId).encode('utf-8'),
                digestmod=hashlib.sha256
                ).digest()
    signature = base64.b64encode(digest).decode()

    client = boto3.client('cognito-idp', region_name='eu-west-1' ) 

    try:
        response = client.sign_up(
                    ClientId=clientId,
                    Username=userObj['user_name'],
                    Password=userObj['password1'],
                    SecretHash=signature,
                    UserAttributes=[
                        {
                            'Name': 'given_name',
                            'Value': userObj['given_name']
                        },
                        {
                            'Name': 'family_name',
                            'Value': userObj['family_name']
                        },
                        {
                            'Name': 'email',
                            'Value': userObj['user_email']
                        },
                        {
                            'Name': 'phone_number',
                            'Value': mobilenumber
                        }
                    ],
                    ValidationData=[
                        {
                            'Name': 'email',
                            'Value': userObj['user_email']
                        },
                    ]
                    ,
                    AnalyticsMetadata={
                        'AnalyticsEndpointId': 'string'
                    },
                    UserContextData={
                        'EncodedData': 'string'
                    }
                )
    except ClientError as error:
        return {"errorcode": error.response['Error']['Code'],
            "errormessage" : error.response['Error']['Message'] }
    except Exception as e:
        return {"errorcode": "Something went wrong. Try later or contact the admin" }
    return {"success": "User registered successfully. "}

error.response ['Error'] ['Code'] sera InvalidPasswordException, UsernameExistsException, etc. Ainsi, dans la fonction principale ou à l'endroit où vous appelez la fonction, vous pouvez écrire la logique pour fournir un message significatif à l'utilisateur.

Un exemple de réponse (error.response):

{
  "Error": {
    "Message": "Password did not conform with policy: Password must have symbol characters",
    "Code": "InvalidPasswordException"
  },
  "ResponseMetadata": {
    "RequestId": "c8a591d5-8c51-4af9-8fad-b38b270c3ca2",
    "HTTPStatusCode": 400,
    "HTTPHeaders": {
      "date": "Wed, 17 Jul 2019 09:38:32 GMT",
      "content-type": "application/x-amz-json-1.1",
      "content-length": "124",
      "connection": "keep-alive",
      "x-amzn-requestid": "c8a591d5-8c51-4af9-8fad-b38b270c3ca2",
      "x-amzn-errortype": "InvalidPasswordException:",
      "x-amzn-errormessage": "Password did not conform with policy: Password must have symbol characters"
    },
    "RetryAttempts": 0
  }
}

Pour plus de références: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp.html#CognitoIdentityProvider.Client.sign_up

0
Haris Np