API Mail pour Python 3

Cette page explique comment utiliser l'API Mail, l'un des anciens services groupés, avec l'environnement d'exécution Python 3 pour l'environnement standard. Votre application peut accéder aux services groupés via le SDK des services App Engine pour Python 3.

Aperçu

Dans Python 3, la fonctionnalité de gestion des e-mails est incluse dans le module google.appengine.api.mail. Ce n'est pas le cas dans Python 2 où le module mail_handlers est fourni par webapp. L'API Mail pour Python 3 peut être utilisée pour recevoir des e-mails et des notifications de non-distribution.

Utiliser l'API Mail

Votre application reçoit un e-mail lorsqu'il est envoyé en tant que corps d'une requête HTTP POST. Pour que l'application puisse gérer les e-mails entrants, elle doit correspondre à l'URL avec le chemin d'accès /_ah/mail/[ADDRESS]. La partie [ADDRESS] du chemin d'accès représente généralement une adresse e-mail ayant le suffixe @<Cloud-Project-ID>.appspotmail.com. Les e-mails envoyés à l'application sous cette forme seront acheminés vers la fonction.

Python 3 ne nécessite pas que l'application spécifie un script de gestionnaire dans le fichier app.yaml. Vous pouvez donc supprimer toutes les sections handler du fichier app.yaml.

Votre fichier app.yaml doit conserver les lignes suivantes :

inbound_services:
- mail
- mail_bounce

Envoyer des e-mails

Vous n'avez pas besoin de modifier la configuration de votre application lorsque vous passez à Python 3. Le comportement, les fonctionnalités et les instructions de configuration concernant l'envoi d'e-mails restent inchangés. Consultez les guides suivants pour en savoir plus :

Recevoir des messages

Pour recevoir des e-mails, vous devez importer le module google.appengine.api.mail et utiliser la classe InboundEmailMessage pour représenter un e-mail. Cette classe doit être instanciée pour récupérer le contenu des e-mails de la requête HTTP entrante.

Auparavant, dans Python 2, les applications pouvaient accéder à la classe InboundEmailMessage en remplaçant la méthode receive() dans le gestionnaire webapp InboundEmailHandler. Cela n'est pas nécessaire dans Python 3. À la place, l'application doit instancier un nouvel objet.

Frameworks Web

Lorsque vous utilisez des frameworks Web Python, le constructeur InboundEmailMessage accepte les octets du corps de la requête HTTP. Il existe plusieurs façons de créer l'objet InboundEmailMessage dans Python 3. Voici des exemples pour les applications Flask et Django :

Python 3 (Flask)

Dans Flask, request.get_data() indique les octets de la requête.

@app.route("/_ah/bounce", methods=["POST"])
def receive_bounce():
    bounce_message = mail.BounceNotification(dict(request.form.lists()))

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    return "OK", 200

Python 3 (Django)

Dans Django, request.body indique les octets du corps de la requête HTTP.

def receive_mail(request):
    message = mail.InboundEmailMessage(request.body)

    print(f"Received greeting for {message.to} at {message.date} from {message.sender}")
    for _, payload in message.bodies("text/plain"):
        print(f"Text/plain body: {payload.decode()}")
        break

    return HttpResponse("OK")

Pour afficher les exemples de code complets de ce guide, consultez GitHub.

Autres frameworks compatibles avec WSGI

Pour les autres frameworks compatibles avec WSGI, nous vous recommandons d'utiliser la même méthode que dans les exemples Flask et Django pour créer InboundEmailMessage. Cette méthode fonctionne lorsque les octets du corps de la requête HTTP sont directement disponibles.

Application WSGI sans framework Web

Si votre application est une application WSGI qui n'utilise pas de framework Web, il est possible que les octets du corps de la requête HTTP ne soient pas directement disponibles. Si les octets du corps de la requête HTTP sont directement disponibles, nous vous recommandons d'utiliser un framework Web Python.

Dans Python 3, une méthode par défaut nommée from_environ est définie pour InboundEmailMessage. Cette méthode de classe utilise le dictionnaire WSGI environ comme entrée et peut être utilisée pour n'importe quelle application WSGI.

Dans l'exemple suivant, notez comment environ est utilisé en tant qu'entrée pour obtenir la valeur mail_message :

Python 3 (application WSGI)

def HelloReceiver(environ, start_response):
    if environ["REQUEST_METHOD"] != "POST":
        return ("", http.HTTPStatus.METHOD_NOT_ALLOWED, [("Allow", "POST")])

    message = mail.InboundEmailMessage.from_environ(environ)

    print(f"Received greeting for {message.to} at {message.date} from {message.sender}")
    for content_type, payload in message.bodies("text/plain"):
        print(f"Text/plain body: {payload.decode()}")
        break

    response = http.HTTPStatus.OK
    start_response(f"{response.value} {response.phrase}", [])
    return ["success".encode("utf-8")]

Recevoir des notifications de non-distribution

Une notification de non-distribution est un message automatisé envoyé par un système de messagerie, qui indique un problème de remise des messages dans votre application. Pour traiter les notifications de non-distribution, votre application doit faire correspondre les chemins d'URL entrants avec le chemin d'accès /_ah/bounce.

Comme InboundEmailMessage, la classe BounceNotification pour Python 2 était accessible en remplaçant la méthode receive() dans le gestionnaire webapp BounceNotificationHandler.

Dans Python 3, l'application doit instancier l'objet BounceNotification, qui peut être créé de plusieurs manières en fonction du framework Web Python utilisé.

Frameworks Web

L'objet BounceNotification est initialisé avec les valeurs récupérées en appelant post_vars.get(key).

Lorsque vous utilisez un framework Web Python, tel que Flask ou Django, le constructeur BounceNotification utilise un dictionnaire nommé post_vars, qui contient la requête POST des données du formulaire. Pour récupérer les données, la méthode get() doit être définie sur l'objet d'entrée. key est une liste de valeurs d'entrée pouvant être lues et récupérées par BounceNotification. Il peut s'agir de l'une des valeurs suivantes :

original-to, original-cc, original-bcc, original-subject, original-text, notification-from, notification-to, notification-cc, notification-bcc, notification-subject, notification-text, raw-message

Dans la plupart des frameworks Web, ces données sont disponibles sous forme de dictionnaire multiple dans l'objet de la requête. La plupart de ces types peuvent être convertis en un dictionnaire associé à des chaînes.

Pour toutes les clés, à l'exception de raw-message, vous pouvez choisir n'importe quelle valeur. Généralement, la valeur est une valeur unique telle qu'une chaîne ou une liste de valeurs (par exemple,{'to': ['bob@example.com', 'alice@example.com']}). La valeur par défaut de tous les champs est une chaîne vide. Ces valeurs renseigneront les propriétés original et notification.

Pour la clé raw-message, la valeur doit être une entrée valide pour le constructeur de EmailMessage. Il peut s'agir d'une valeur unique ou d'une liste de valeurs uniques. La clé raw-message permet d'initialiser la propriété original_raw_message de l'objet.

Python 2 (webapp2)

class LogBounceHandler(BounceNotificationHandler):
    def receive(self, bounce_message):
        logging.info('Received bounce post ... [%s]', self.request)
        logging.info('Bounce original: %s', bounce_message.original)
        logging.info('Bounce notification: %s', bounce_message.notification)

Python 3 (Flask)

Dans Flask, request.form de type werkzeug.datastructures.MultiDict fournit les variables POST. Cependant, la méthode get() pour ce type ne renvoie qu'une seule valeur, même si plusieurs valeurs sont présentes pour la clé.

Pour obtenir toutes les valeurs correspondant à une clé, l'application doit appeler dict(request.form.lists()) pour générer un dictionnaire dans lequel chaque valeur est une liste.

@app.route("/_ah/bounce", methods=["POST"])
def receive_bounce():
    bounce_message = mail.BounceNotification(dict(request.form.lists()))

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    return "OK", 200

Python 3 (Django)

Dans Django, request.POST de type django.http.QueryDict fournit les variables POST. Cependant, la méthode get() pour ce type ne renvoie qu'une seule valeur, même si plusieurs valeurs sont présentes pour la clé.

Pour obtenir toutes les valeurs correspondant à une clé, l'application doit appeler dict(request.POST.lists()) pour générer un dictionnaire dans lequel chaque valeur est une liste.

def receive_bounce(request):
    bounce_message = mail.BounceNotification(dict(request.POST.lists()))

    # Do something with the message
    print(f"Bounce original: {bounce_message.original}")
    print(f"Bounce notification: {bounce_message.notification}")

    return HttpResponse("OK")

Application WSGI sans framework Web

Si votre application est une application WSGI qui n'utilise pas de framework Web, il est possible que les variables de formulaire de la requête HTTP POST ne soient pas directement disponibles dans un dictionnaire.

Dans Python 3, une méthode par défaut nommée from_environ est définie pour BounceNotification. Cette méthode de classe utilise le dictionnaire WSGI environ comme entrée et peut être utilisée pour n'importe quelle application WSGI.

Dans l'exemple suivant, notez comment environ est utilisé en tant qu'entrée pour obtenir la valeur bounce_message :

Python 3

def BounceReceiver(environ, start_response):
    if environ["REQUEST_METHOD"] != "POST":
        return ("", http.HTTPStatus.METHOD_NOT_ALLOWED, [("Allow", "POST")])

    bounce_message = mail.BounceNotification.from_environ(environ)

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    # Return suitable response
    response = http.HTTPStatus.OK
    start_response(f"{response.value} {response.phrase}", [])
    return ["success".encode("utf-8")]

Exemples de code

Pour afficher les exemples de code complets de ce guide, consultez GitHub.