- Notifications
You must be signed in to change notification settings - Fork264
Send email in Python conveniently for gmail using yagmail
License
kootenpv/yagmail
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
For the asynchronous asyncio version, look here:https://github.com/kootenpv/aioyagmail
The goal here is to make it as simple and painless as possible to send emails.
In the end, your code will look something like this:
importyagmailyag=yagmail.SMTP('mygmailusername','mygmailpassword')contents= ['This is the body, and here is just text http://somedomain/image.png','You can find an audio file attached.','/local/path/song.mp3']yag.send('to@someone.com','subject',contents)
In 2020, I personally prefer: using anApplication-Specific Password
Section | Explanation |
---|---|
Install | Find the instructions on how to install yagmail here |
Start a connection | Get started |
Usability | Shows some usage patterns for sending |
Recipients | How to send to multiple people, give an alias or send to self |
Magical contents | Really easy to send text, html, images and attachments |
Attaching files | How attach files to the email |
DKIM Support | Add DKIM signature to your emails with your private key |
Feedback | How to send me feedback |
Roadmap (and priorities) | Yup |
Errors | List of common errors for people dealing with sending emails |
For Python 2.x and Python 3.x respectively:
pipinstallyagmail[all]pip3installyagmail[all]
As a side note,yagmail
can now also be used to send emails from the command line.
yag=yagmail.SMTP('mygmailusername','mygmailpassword')
Note that this connection is reusable, closable and when it leaves scope it willclean up after itself in CPython.
Astilgovi points out in#39, SMTP does not automatically close inPyPy. The context managerwith
should be used in that case.
Defining some variables:
to='santa@someone.com'to2='easterbunny@someone.com'to3='sky@pip-package.com'subject='This is obviously the subject'body='This is obviously the body'html='<a href="https://pypi.python.org/pypi/sky/">Click me!</a>'img='/local/file/bunny.png'
All variables are optional, and know that not evento
is required (you'll send an email to yourself):
yag.send(to=to,subject=subject,contents=body)yag.send(to=to,subject=subject,contents= [body,html,img])yag.send(contents= [body,img])
Furthermore, if you do not want to be explicit, you can do the following:
yag.send(to,subject, [body,img])
It is also possible to send to a group of people by providing a list of email strings rather than a single string:
yag.send(to=to)yag.send(to= [to,to2])# List or tuples for emailadresses *without* aliasesyag.send(to= {to :'Alias1'})# Dictionary for emailaddress *with* aliasesyag.send(to= {to :'Alias1',to2 :'Alias2'}
Giving noto
argument will send an email to yourself. In that sense,yagmail.SMTP().send()
can already send an email.Be aware that if no explicitto = ...
is used, the first argument will be used to send to. Can be avoided like:
yag.send(subject='to self',contents='hi!')
Note that by default all email addresses are conservatively validated usingsoft_email_validation==True
(default).
It is even safer to use Oauth2 for authentication, as you can revoke the rights of tokens.
This is one of the best sources, upon which the oauth2 code is heavily based.
The code:
yag=yagmail.SMTP("user@gmail.com",oauth2_file="~/oauth2_creds.json")yag.send(subject="Great!")
It will prompt for agoogle_client_id
and agoogle_client_secret
, when the file cannot be found. These variables can be obtained followingthe previous link.
After you provide them, a link will be shown in the terminal that you should followed to obtain agoogle_refresh_token
. Paste this again, and you're set up!
Note that people who obtain the file can send emails, but nothing else. As soon as you notice, you can simply disable the token.
Your Google Cloud Platform project's OAuth consent screen must be in"In production" publishing status before authorizing to not have the authorization expire after 7 days. See status athttps://console.cloud.google.com/apis/credentials/consent
Your OAuthclient ID must be of type "Desktop". Check athttps://console.cloud.google.com/apis/credentials
Thecontents
argument will be smartly guessed. It can be passed a string (which will be turned into a list); or a list. For each object in the list:
- If it is a dictionary it will assume the key is the content, and the value is an alias (only for images currently!)e.g. {'/path/to/image.png' : 'MyPicture'}
- It will try to see if the content (string) can be read as a file locally,e.g. '/path/to/image.png'
- if impossible, it will check if the string is valid htmle.g.
<h1>This is a big title</h1>
- if not, it must be text.e.g. 'Hi Dorika!'
Note that local files can be html (inline); everything else will be attached.
Local files require to have an extension for their content type to be inferred.
As of version 0.4.94,raw
andinline
have been added.
raw
ensures a string will not receive any "magic" (inlining, html, attaching)inline
will make an image appear in the text.
There are multiple ways to attach files in theattachments
parameter (in addition to magicalcontents
parameter).
- One can pass a list of paths i.e.
yag.send(to=recipients,subject=email_subject,contents=contents,attachments=['path/to/attachment1.png','path/to/attachment2.pdf','path/to/attachment3.zip'])
- One can pass an instance of
io.IOBase
.
withopen('path/to/attachment','rb')asf:yag.send(to=recipients,subject=email_subject,contents=contents,attachments=f )
In this examplef
is an instance of_io.BufferedReader
a subclass of the abstract classio.IOBase
.
f
has in this example the attribute.name
, which is used by yagmail as filename as well as to detect the correct MIME-type.Not allio.IOBase
instances have the.name
attribute in which case yagmail names the attachmentsattachment1
,attachment2
, ... without a file extension!Therefore, it is highly recommended setting the filename with extension manually e.g.f.name = 'my_document.pdf'
A real-world example would be if the attachment is retrieved from a different source than the disk (e.g. downloaded from the internet oruploaded by a user in a web-application)
To send emails with dkim signature, you need to install the package with all related packages.
pip install yagmail[all]# orpip install yagmail[dkim]
Usage:
fromyagmailimportSMTPfromyagmail.dkimimportDKIMfrompathlibimportPath# load private key from file/secrets managerprivate_key=Path("privkey.pem").read_bytes()dkim_obj=DKIM(domain=b"a.com",selector=b"selector",private_key=private_key,include_headers=[b"To",b"From",b"Subject"],# To include all default headers just pass None instead# include_headers=None,)yag=SMTP(dkim=dkim_obj)# all the rest is the same
I'll try to respond to issues within 24 hours at Github.....
And please send me a line of feedback withSMTP().feedback('Great job!')
:-)
Added possibility of ImageOptional SMTP arguments should go with **kwargs to my SMTPCC/BCC (high)Custom names (high)Allow send to return a preview rather than to actually sendJust use attachments in "contents", being smart guessed (high, complex)Attachments (contents) in a list so they actually define the order (medium)Use lxml to see if it can parse the html (low)Added tests (high)Allow caching of content (low)Extra other types (low)(for example, mp3 also works, let me know if something does not work)Probably a naming issue with content type/default typeChoose inline or not somehow (high)Make lxml module optional magic (high)Provide automatic fallback for complex content(medium)(should work)yagmail
as a command on CLI upon installAddedfeedback
function on SMTP to be able to send me feedback directly :-)Added the option to validate emailaddresses...however, I'm unhappy with the error handling/logging of wrong emailsLogging count & mail capability (very low)Add documentation to exception classes (low)addraw
andinline
oauth2- ~~Travis CI integration ~~
- ~~ Add documentation to all functions (high, halfway) ~~
- Prepare for official 1.0
- Go over documentation again (medium)
- Allow
.yagmail
file to contain more parameters (medium) - Add option to shrink images (low)
smtplib.SMTPException: SMTP AUTH extension not supported by server
SMTPAuthenticationError: Application-specific password required
YagAddressError: This means that the address was given in an invalid format. Note that
From
can either be a string, or a dictionary where the key is anemail
, and the value is analias
{'sample@gmail.com': 'Sam'}. In the case of 'to', it can either be a string (email
), a list of emails (email addresses without aliases) or a dictionary where keys are the email addresses and the values indicate the aliases.YagInvalidEmailAddress: Note that this will only filter out syntax mistakes in emailaddresses. If a human would think it is probably a valid email, it will most likely pass. However, it could still very well be that the actual emailaddress has simply not be claimed by anyone (so then this function fails to devalidate).
Click to enable the email for being used externallyhttps://www.google.com/settings/security/lesssecureapps
Make sure you have a working internet connection
If you get an
ImportError
try to install withsudo
, see issue #13
If you likeyagmail
, feel free (no pun intended) to donate any amount you'd like :-)