[PYTHON] Définir la réponse de l'API client boto3 dans la classe de données

introduction

Je jouais avec boto3 pour étudier. J'ai pensé qu'il serait préférable de stocker les informations sur les ressources obtenues en utilisant boto3 dans une classe de données et d'y faire référence (la raison sera décrite plus tard), je vais donc l'écrire comme un article comme une découverte.

Qu'est-ce que boto3!?

En bref, c'est un SDK pour travailler avec des ressources AWS en python. Il est divisé en API de bas niveau (client) et API de haut niveau (resorce). Il existe d'innombrables autres articles connexes, donc si vous voulez en savoir plus, veuillez les consulter.

Cette fois, nous utiliserons l'API de bas niveau.

Qu'est-ce qu'une classe de données!?

Il fournit un décorateur qui attribue dynamiquement des méthodes spéciales associées à des classes telles que \ _ \ _ init \ _ \ _ ().

Une classe avec le constructeur suivant

class Wanchan_Nekochan():
    def __init__(self, cat:str, dog:str):
     self.cat =cat
      self.dog = dog

Vous pourrez écrire intelligemment sans un tel constructeur.

@dataclass
class Wanchan_Nekochan():
    cat: str
    dog: str

https://docs.python.org/ja/3/library/dataclasses.html

Par rapport aux tuples nommés qui ont des fonctions similaires, il existe les différences suivantes. -Namedtuple devient immuable (non modifiable) après l'instanciation. dataclass est mutable (modifiable) par défaut, mais si vous passez l'argument gelé comme vrai Devenez immuable. ・ La classe de données lit les données un peu plus rapidement (vérification requise)

Exercice

Cette fois, pour obtenir la liste des compartiments de S3, utilisez la méthode list_buckets () de la classe S3.Client. (Référence) Référence officielle https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.list_buckets

La définition de la réponse est le type de dictionnaire suivant.

{
    'Buckets': [
        {
            'Name': 'string',
            'CreationDate': datetime
        },
    ],
    'Owner': {
        'DisplayName': 'string',
        'ID': 'string'
    },
    "ResponseMetadata": {
        "RequestId": str,
        "HTTPStatusCode": int,
        "HTTPHeaders": dict,
        "RetryAttempts": int
    }
}

Lors de l'obtention d'informations à partir de la réponse renvoyée, la clé sera spécifiée sous forme de chaîne de caractères comme indiqué ci-dessous.

s3_client = session.client('s3')
data=s3_client.list_buckets()

status_code = data["ResponseMetadata"]["HTTPStatusCode"]
display_name = data["Owner"]["DisplayName"]

Étant donné que la clé est spécifiée par une chaîne de caractères, il existe un risque de fautes d'orthographe car la fonction de saisie semi-automatique de l'EDI ne peut pas être utilisée et il existe des problèmes gênants tels que le fait de ne pas savoir jusqu'à ce que vous vous référiez au type des données.

Si vous définissez à l'avance la définition de la réponse en tant que classe de données, l'accès aux points est possible, de sorte que l'achèvement des entrées devient possible et les indications de type de mypy peuvent être utilisées avec rigueur.

Il n'y a aucune raison de ne pas utiliser de dataclass !!!

Donc ...

Source pour le moment

from dataclasses import dataclass
from typing import List
from datetime import datetime

import boto3
from dacite import from_dict


session = boto3.session.Session(profile_name='s3_test')
#BasesClient est dans la portée mondiale pour éviter de lancer des API plusieurs fois
#Implémenter en singleton
s3_client = session.client('s3')

@dataclass
class Boto3_Response():
    RequestId: str
    HostId: str
    HTTPStatusCode: int
    HTTPHeaders: Dict
    RetryAttempts: int

@dataclass
class Inner_Owner():
    DisplayName: str
    ID: str

@dataclass
class Inner_Buckets():
    Name: str
    CreationDate: datetime

@dataclass
class S3_LIST():
    ResponseMetadata: Boto3_Response
    Owner: Inner_Owner
    Buckets: List[Inner_Buckets]

    @classmethod
    def make_s3_name_list(cls):
        return from_dict(data_class=cls, data=s3_client.list_buckets())


s3_list_response = S3_LIST.make_s3_name_list()

#Code d'état
print(s3_list_response.ResponseMetadata.HTTPStatusCode) #200
#Le nom du propriétaire
print(s3_list_response.Owner.DisplayName) # nikujaga-kun
# ID
print(s3_list_response.Owner.ID) 
#Liste de seau
print(*[bucket.Name for bucket in s3_list_response.Buckets])

Explication ci-dessous

from dacite import from_dict

Ici, nous importons une bibliothèque tierce appelée dacite. dacite (je l'ai lu comme "de") est simplement une bibliothèque pour passer des types de dictionnaire à des classes de données imbriquées et les instancier. https://pypi.org/project/dacite/

@dataclass
class Boto3_Response():
    RequestId: str
    HostId: str
    HTTPStatusCode: int
    HTTPHeaders: Dict
    RetryAttempts: int

@dataclass
class Inner_Owner():
    DisplayName: str
    ID: str

@dataclass
class Inner_Buckets():
    Name: str
    CreationDate: datetime

@dataclass
class S3_LIST():
    ResponseMetadata: Boto3_Response
    Owner: Inner_Owner
    Buckets: List[Inner_Buckets]

    @classmethod
    def make_s3_name_list(cls):
        return from_dict(data_class=cls, data=s3_client.list_buckets())

Dans la définition ci-dessus de S3_LIST, différentes classes de données Boto3_Response, Inner_Owner, Inner_Buckets sont définies comme des types d'attributs. Si vous passez la réponse de list_buckets à S3_LIST telle quelle sans utiliser from_dict de dacite

@classmethod
   def make_s3_name_list(cls):
      return cls(**s3_client.list_buckets())

Cela ressemble à ceci, mais comme il est passé comme un type de dictionnaire au lieu d'une instance de classe, il se met en colère s'il n'y a pas un tel attribut.

s3_list_response = S3_LIST.make_s3_name_list()

#Mettez-vous en colère contre l'erreur d'attribut ici
print(s3_list_response.ResponseMetadata.HTTPStatusCode)

# 'dict' object has no attribute 'HTTPStatusCode'

Si vous essayez de faire quelque chose de bien ici simplement en l'incorporant, ce sera difficile comme ça, alors J'utilise dacite comme bibliothèque qui fait du bon travail. Vous devriez monter indéfiniment sur l'épaule du géant.

finalement

la classe de données est bonne

Recommended Posts

Définir la réponse de l'API client boto3 dans la classe de données
Obtenez les données de l'API Google Fit en Python
Obtenez des données Youtube en Python à l'aide de l'API Youtube Data
SELECT des données à l'aide de la bibliothèque cliente avec BigQuery
Mettez les données AWS dans Google Spreadsheet avec boto + gspread
[Route vers Python intermédiaire] Définir la fonction __getattr__ dans la classe