EZGmail¶
A Pythonic interface to the Gmail API that actually works as of November 2019.
Installation¶
EZGmail can be installed from PyPI using pip:
pip install ezgmail
On macOS and Linux, installing EZGmail for Python 3 is done with pip3:
pip3 install ezgmail
If you run into permissions errors, try installing with the –user option:
pip install --user ezgmail
You must also sign up for a Gmail account at https://gmail.com/. Then, go to https://developers.google.com/gmail/api/quickstart/python/, click the Enable the Gmail API button on that page, and fill out the form that appears.
After you’ve filled out the form, the page will present a link to the credentials.json file, which you’ll need to download and place in the same folder as your .py file. The credentials.json file contains the Client ID and Client Secret information, which you should treat the same as your Gmail password and not share with anyone else.
Make sure you set your current working directory to the same folder that credentials.json is in and that you’re connected to the internet. Running import ezgmail (which calls the ezgmail.init() function) will open your browser to a Google sign-in page.
Enter your Gmail address and password. The page may warn you “This app isn’t verified,” but this is fine; click Advanced and then Go to Quickstart (unsafe). (If you write Python scripts for others and don’t want this warning appearing for them, you’ll need to learn about Google’s app verification process, which is beyond the scope of this documentaiton.) When the next page prompts you with “Quickstart wants to access your Google Account,” click Allow and then close the browser.
A token.json file will be generated to give your Python scripts access to the Gmail account you entered. The browser will only open to the login page if it can’t find an existing token.json file. With credentials.json and token.json, your Python scripts can send and read emails from your Gmail account without requiring you to include your Gmail password in your source code.
Quickstart¶
To log in to your Gmail account, simple import EZGmail after setting up the credentials.json and token.json files:
>>> import ezgmail
If you’d like to log in as a different user (based on different credentials.json and token.json files), call
ezgmail.init()
and pass the filenames for the credential and token files you generated.
>>> ezgmail.init(tokenFile='token.json', credentialsFile='credentials.json')
At this point, the EMAIL_ADDRESS
global variable will contain a string of the logged in user, and LOGGED_IN
is
set to True
. (Otherwise, they are set to None
and False
, respectively.)
>>> ezgmail.EMAIL_ADDRESS
'test.sweigart@gmail.com'
>>> ezgmail.LOGGED_IN
True
To send an email from the logged in account, call ezgmail.send()
:
>>> ezgmail.send('recipient@example.com', 'Subject Line', 'Hello, this is the body of the message.')
To get a list of all the email threads of unread emails, call ezgmail.unread()
. This returns a list of
GmailThread
objects. Each GmailThread
contains a messages
attribute which is a list of GmailMessage
objects. Each GmailMessage
object has attributes sender
, timestamp
, subject
, body
:
>>> ezgmail.unread()
[<GmailThread len=4 snippet='Quickstart was granted access to your Google Account test.sweigart@gmail.com If you did not grant access, you should check this activity and secure your account. Check activity You received this email'>]
>>> unreadThreads = ezgmail.unread()
>>> unreadThreads
[<GmailThread len=4 snippet='Quickstart was granted access to your Google Account test.sweigart@gmail.com If you did not grant access, you should check this activity and secure your account. Check activity You received this email'>]
>>> thread = unreadThreads[0]
>>> thread.messages
[<ezgmail.GmailMessage object at 0x000002B7F1FD8A90>, <ezgmail.GmailMessage object at 0x000002B7F1FD80B8>, <ezgmail.GmailMessage object at 0x000002B7F1FD8A58>, <ezgmail.GmailMessage object at 0x000002B7F1FD8160>]
>>> thread.messages[0].sender
'Alice Smith <alice@example.com>'
>>> thread.messages[0].timestamp
datetime.datetime(2019, 11, 11, 10, 7, 55)
>>> thread.messages[0].subject
'Good to meet you!'
>>> thread.messages[0].body
"It was good to meetup!\r\n\r\n\r\nWe should hang out again soon.\r\n"
You can call ezgmail.recent()
to get the recent email threads as a list of GmailThread
objects.
You can search for messages by calling ezgmail.search('query string')
, which also returns a list of GmailThread
objects. The query string is exactly the same as you would type in the Gmail search box, and you can use the search
operatives for it too:
ezgmail.search('label:UNREAD')
ezgmail.search('from:al@inventwithpython.com')
ezgmail.search('subject:hello')
ezgmail.search('has:attachment')
More search operatives are described at https://support.google.com/mail/answer/7190?hl=en
By default, EZGmail sends messages as plaintext. You can send HTML emails by passing 'html'
for the mimeSubtype
parameter in send()
. (By default, this parameter is set to 'plain'
.) This email has “Hello” appear in bold and “body” appear italicized:
>>> ezgmail.send('recipient@example.com', 'Subject Line', '<strong>Hello</strong>, this is the <em>body</em> of the message.', mimeSubtype='html')
Accessing an email or thread doesn’t mark it as unread automatically. You must do that yourself by calling the markAsRead()
method of the GmailThread
or GmailMessage
object. (There is also a corresponding markAsUnread()
function.) You can also call ezgmail.markAsRead()
and pass it a list of GmailThread
or GmailMessage
objects.
>>> import ezgmail
>>> unreadThreads = ezgmail.unread()
>>> ezgmail.markAsRead(unreadThreads) # Marks all the GmailThread objects in the unreadThreads list as read.
>>> # Or you can do:
>>> for unreadThread in unreadThreads:
... unreadThread.markAsRead() # Mark the individual GmailThread objects as read.
These two functions make add/remove the 'UNREAD'
label using EZGmail’s addLabel()
and removeLabel()
functions:
>>> import ezgmail
>>> unreadThreads = ezgmail.unread()
>>> ezgmail.removeLabel(unreadThreads, 'UNREAD') # Also marks threads as read.
>>> ezgmail.addLabel(unreadThreads, 'UNREAD') # Marks them as unread again.
>>> # Or you can do:
>>> for unreadThread in unreadThreads:
... unreadThread.removeLabel(unreadThreads, 'UNREAD') # Mark the individual GmailThread objects as read.
(Currently EZGmail doesn’t have functions for adding/deleting/managing custom labels.)
The trash()
method deletes the message or messages in a GmailMessage
or GmailThread
object:
>>> import ezgmail
>>> threads = ezgmail.search('mancala')
>>> threads[0].trash() # Move the entire first thread to the Trash folder.
To view the attachments of an email, look at the GmailMessage
object’s attachments
dictionary. The keys are the filenames of the attachments. You can either call the downloadAttachment()
or downloadAllAttachments()
methods:
>>> import ezgmail
>>> threads = ezgmail.search('See the attached files')
>>> threads[0].messages[0].attachments
>>> import pprint
>>> pprint.pprint(threads[0].messages[0].attachments)
{'a.png': {'id': 'ANGjdJ8eLDbjBpFTfvpuQ2HfR_iwp59XLUIl-IHW8eJcexMsxBYoPCZAXcX16rnqcbJZTknF5r3GmnM1W9n4vAE1oiVgUa4S4zBmNs7rd5PzFwLjO2vU3hp3_9SEZv-KBqVxi9nuNjarxhFqp3mxw6E5mqEYmFOYtT7Gx6CZbLaJuUox9GaWu-W9B4-XPDjwKkEfCdJ21FlOl-CsC6isZgD2Vh-ghh1haZN_2sifccznLv61ZW_KmqPKFcV1j7cXMQVqWU7bkgdH8do4Msc3QsG2ly_PNRid4-7gihsXaLI1ko_j3LSvsoLHFP3edhxh6YKQ2OdMhyZh5lqjmfT1TXgSo7hY16P_ScDO5MnWvmKscf_Hm5y5D4DHfwOq4--Otivoq2WVkVucVUJBkAoB',
'size': 833609},
'b.png': {'id': 'ANGjdJ_WYMmPmy2Dd2VBgvVoLAd1p3ARxGXKIzVfKqAiLhvKSBmEowYqFCdHbMJYlDZy4IWBGLg0eQCllMI0icqamM7vfMxBW2irJVogLM6SUT9cIcJFMSF7UhzU2I26bho086J7NjnX5u4kqYj_LHchowO56vTdKLRRsaJ2gfW0esz3cDFZzvthdR4wyBKEIeCJv7OJmFiaJIRf9f1KmFfKPLo9GZSyD2RMXdd6Qa2M3uN9pgT6sZ-OQx3e6aNDAKWh5GCeSiuIt_Z7GsDCdzVJjakMJx5FRFhp5zIck0p04AHnYhKfy1BipWmf7G-DAKzgJHAhFimBVUIBeFsHrqEGxDlevD7lK4ZBeb8cluSmYyEsRkSPSMYMlp-x1GVw25gqMnMVkGMKPfwj38iB',
'size': 335911}}
>>> threads[0].messages[0].downloadAttachment('a.png') # Download to current working directory.
>>> threads[0].messages[0].downloadAttachment('b.png', '/path/to/save/in')
>>> threads[0].messages[0].downloadAllAttachments() # Easier way to save all attachments.
The API section contains complete documentation.
API¶
EZGmail - A Pythonic interface to the Gmail API that actually works as of October 2022.
-
exception
ezgmail.
EZGmailException
¶ The base class for all EZGmail-specific problems. If the
ezgmail
module raises something that isn’t this or a subclass of this exception, you can assume it is caused by a bug in EZGmail.
-
exception
ezgmail.
EZGmailTypeError
¶ The EZGmail module equivalent of ValueError. This exception is raised when a parameter of an incorrect type is passed to an EZGmail function.
-
exception
ezgmail.
EZGmailValueError
¶ The EZGmail module equivalent of ValueError. This exception is raised when a parameter of an incorrect value (but not necessarily an incorrect type) is passed to an EZGmail function.
-
class
ezgmail.
GmailMessage
(messageObj)¶ Represents a Gmail messages. These objects are returned by the users.messages.get() API call. They contain all the header/subject/body information of a single email.
The
sender
attribute has a string like'Google <no-reply@accounts.google.com>'
.The
recipient
attribute has a string likeal@inventwithpython.com'
.The
subject
attribute contains a string of the subject line.The
body
attribute contains text up to the quoted “reply” text that begins with “On Sun, Jan 1, 2018 at 12:00 PM al@inventwithpython.com wrote:” part.The
originalBody
attribute contains the full message body.The
timestamp
attribute contains adatetime.datetime
object of the internal message creation timestamp (epoch ms), which determines ordering in the inbox.The
snippet
attribute contains a string of up to the first 200 characters of the body.These attributes are based on the Gmail API: https://developers.google.com/gmail/api/v1/reference/users/messages
-
addLabel
(label)¶ Add the label
label
to every message in this thread.
-
downloadAllAttachments
(downloadFolder='.', overwrite=True)¶ Download all of the attachments in this message to the local folder
downloadFolder
. Ifoverwrite
isTrue
, existing local files will be overwritten by attachments with the same filename.
-
downloadAttachment
(filename, downloadFolder='.', duplicateIndex=0)¶ Download the file attachment in this message with the name
filename
to the local folderdownloadFolder
. If there are multiple attachments with the same name,duplicateIndex
needs to be passed to specify which attachment to download.
-
markAsRead
()¶ Mark this message as read. (This does the same thing as removing the UNREAD label from the message.)
-
markAsUnread
()¶ Mark this message as unread. (This does the same thing as adding the UNREAD label to the message.)
-
removeLabel
(label)¶ Remove the label
label
from every message in this thread, if it’s there.
-
reply
(body, attachments=None, cc=None, bcc=None, mimeSubtype='plain')¶ Like the send() function, but replies to the last message in this thread.
-
replyAll
(body, attachments=None, cc=None, bcc=None, mimeSubtype='plain')¶ Like the send() function, but replies to the last message in this thread.
-
trash
()¶ Move this message to the Trash folder. It will be automatically removed in 30 days.
-
-
class
ezgmail.
GmailThread
(threadObj)¶ Represents a thread of Gmail messages. These objects are returned by the users.threads.get() API call. They contain references to a list of GmailMessage objects.
-
addLabel
(label)¶ Add the label
label
to every message in this thread.
-
latestTimestamp
()¶ The
-
markAsRead
()¶ Mark every message in this thread as read. (This does the same thing as removing the UNREAD label from the messages.)
-
markAsUnread
()¶ Mark every message in this thread as unread. (This does the same thing as adding the UNREAD label to the messages.)
-
messages
¶ The GmailMessage objects of the emails in this thread, starting from the oldest at index 0 to the most recent.
-
removeLabel
(label)¶ Remove the label
label
from every message in this thread, if it’s there.
-
senders
()¶ Returns a list of strings of the senders in this thread, from the oldest at index 0 to the most recent.
-
text
¶ A list of strings, where each string is the message of a single email in this thread of emails, starting from the oldest at index 0 to the most recent.
-
trash
()¶ Move every message in this thread to the Trash folder. It will be automatically removed in 30 days.
-
-
ezgmail.
init
(userId='me', tokenFile='token.json', credentialsFile='credentials.json', _raiseException=True)¶ This function must be called before any other function in EZGmail (and is automatically called by them anyway, so you don’t have to explicitly call this yourself).
This function populates the
SERVICE_GMAIL
global variable used in all Gmail API calls. It also populatesEMAIL_ADDRESS
with a string of the Gmail account’s email address (and sets the globalLOGGED_IN
toTrue
). This account is determined by the credentials.json file, downloaded from Google, and token.json. If thetokenFile
file hasn’t been generated yet, this function will open the browser to a page to let the user log in to the Gmail account that this module will use.If you want to switch to a different Gmail account, call this function again with a different
tokenFile
andcredentialsFile
arguments.
-
ezgmail.
recent
(maxResults=25, userId='me')¶ Return a list of
GmailThread
objects for the most recent emails. Essentially a wrapper forsearch()
.First index is the most recent.
-
ezgmail.
removeQuotedParts
(emailText)¶ Returns the text in
emailText
up to the quoted “reply” text that begins with “On Sun, Jan 1, 2018 at 12:00 PM al@inventwithpython.com wrote:” part.
-
ezgmail.
search
(query, maxResults=25, userId='me')¶ Returns a list of GmailThread objects that match the search query.
The
query
string is exactly the same as you would type in the Gmail search box, and you can use the search operatives for it too:- label:UNREAD
- from:al@inventwithpython.com
- subject:hello
- has:attachment
More are described at https://support.google.com/mail/answer/7190?hl=en
-
ezgmail.
send
(recipient, subject, body, attachments=None, sender=None, cc=None, bcc=None, mimeSubtype='plain', _threadId=None)¶ Sends an email from the configured Gmail account.
Note that the
sender
argument seems to be ignored by Gmail, which uses the account’s actual email address.TODO - Add additional details to this docstring.
-
ezgmail.
summary
(gmailObjects, printInfo=True)¶ Prints out a summary of the
GmailThread
orGmailMessage
in thegmailObjects
list.
-
ezgmail.
unread
(maxResults=25, userId='me')¶ Return a list of
GmailThread
objects for unread emails. Essentially a wrapper forsearch()
.