This page describes how to use the Mail API, one of the legacy bundled services, with the Python runtime for the standard environment. Your app can access the bundled services through the App Engine services SDK for Python.
Overview
In Python, the mail handling functionality is included in the
google.appengine.api.mail
module. This is different from Python 2, where the mail_handlers
module was provided by webapp. The Mail
API for Python can be used to receive emails and bounce notifications.
Using the Mail API
Your app receives mail when an email is sent as the request body in an HTTP POST
request. For the app to handle incoming emails, the app needs to match the URL
with the path /_ah/mail/[ADDRESS]
. The [ADDRESS]
part of
the path is usually an email address with the suffix
@<Cloud-Project-ID>.appspotmail.com
. Emails sent to the app in this format
will be routed to the function.
Python doesn't require the app to specify a handler script in the app.yaml
file, so you can remove all handler
sections in app.yaml
.
Your app.yaml
file should keep the following lines:
inbound_services:
- mail
- mail_bounce
Sending mail
You do not need to make changes to your app's configuration when upgrading to Python. The behavior, features, and setup instructions for sending mail remains the same. Refer to the following guides for more details:
Receiving mail
To receive mail, you need to import the google.appengine.api.mail
module and
use the InboundEmailMessage
class to represent an email. This class needs to
be instantiated to retrieve the email content from the incoming HTTP request.
Previously in Python 2, apps could access the InboundEmailMessage
class by
overriding the receive()
method in the webapp handler InboundEmailHandler
.
This is not needed in Python; instead, the app needs to instantiate a new object.
Web frameworks
When using Python web frameworks, the InboundEmailMessage
constructor takes in
the bytes of the HTTP request body. There are multiple ways to create the
InboundEmailMessage
object in Python. The following are examples for Flask
and Djago apps:
Python 3 (Flask)
In Flask, request.get_data()
gives the request bytes.
Python 3 (Django)
In Django, request.body
gives the bytes of the HTTP request body.
To view the complete code samples from this guide, see GitHub.
Other WSGI-compliant frameworks
For other WSGI-compliant frameworks, we recommend using the same method as the
Flask and Django examples to create the InboundEmailMessage
. This method works
when the bytes of the HTTP request body are directly available.
WSGI app without a web framework
If your app is a WSGI app that does not use a web framework, it is possible that the bytes of the HTTP request body is not directly available. If the bytes of the HTTP request body are directly available, we recommend that you use a Python web framework.
In Python, a factory method named from_environ
is defined for
InboundEmailMessage
. This method is a class method that takes the
WSGI environ
dictionary
as the input, and can be used for any WSGI application.
In the following example, notice how environ
is taken in as an input to get the
mail_message
:
Python 3 (WSGI app)
Receiving bounce notifications
A bounce notification is an automated message from an email system that
indicates a problem with your app's message delivery. To process bounce
notifications, your app needs to match incoming URL paths with the /_ah/bounce
path.
Like InboundEmailMessage
, the BounceNotification
class for Python 2 was
accessible by overriding the receive()
method in the webapp handler
BounceNotificationHandler
.
In Python, the app needs to instantiate the BounceNotification
object,
which can be created in multiple ways depending on the Python web framework used.
Web frameworks
The BounceNotification
object is initialized with the values that are
retrieved by calling post_vars.get(key)
.
When using a Python web framework, like Flask or Django, the
BounceNotification
constructor takes in a dictionary named post_vars
, which
contains the POST request of the form data. To retrieve the data, the method
get()
needs to be defined on the input object. The key
is a list of input
values that can be read and retrieved by BounceNotification
and can be any of
the following:
original-to, original-cc, original-bcc, original-subject, original-text, notification-from, notification-to, notification-cc, notification-bcc, notification-subject, notification-text, raw-message
In most web frameworks, this data is available as a multi dictionary in the request object. Most of these types can be converted into a dictionary keyed by strings.
For all keys except raw-message
, the value can be anything. Usually, the value is
either a single value such as a string, or a list of values, such as
{'to': ['bob@example.com', 'alice@example.com']}
. The default value for all fields is
an empty string. These values will populate the original
and notification
properties.
For the raw-message
key, the value needs to be a valid input to the constructor of
EmailMessage
.
It can either be a single value or a single valued list. The raw-message
key
is used to initialize the original_raw_message
property of the object.
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)
In Flask,
request.form
of type werkzeug.datastructures.MultiDict
gives the POST variables. However, the get()
method for this type only returns
a value, even if there are multiple values for the key is present.
To get all values corresponding to a key,
the app needs to call
dict(request.form.lists())
,
which results in a dictionary where each value is a list.
Python 3 (Django)
In Django, request.POST
of type django.http.QueryDict
gives the POST variables. However, the get()
method for this type only returns
a value, even if there are multiple values for the key is present.
To get all values corresponding to a key,
the app needs to call dict(request.POST.lists())
,
which results in a dictionary where each value is a list.
WSGI app without a web framework
If your app is a WSGI app that does not use a web framework, it is possible that the form variables of the HTTP POST request are not directly available in a dictionary.
In Python, a factory method named from_environ
is defined for
BounceNotification
. This method is a class method that takes the WSGI
environ
dictionary as the input, and can be used for any WSGI application.
In the following example, notice how environ
is taken in as an input to get the
bounce_message
:
Python 3
Code samples
To view the complete code samples from this guide, see GitHub.