• Stars
    star
    2,579
  • Rank 17,043 (Top 0.4 %)
  • Language
    Python
  • License
    MIT License
  • Created about 9 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Send email in Python conveniently for gmail using yagmail

yagmail -- Yet Another GMAIL/SMTP client

Join the chat at https://gitter.im/kootenpv/yagmail PyPI PyPI

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:

import yagmail
yag = 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('[email protected]', 'subject', contents)

In 2020, I personally prefer: using an Application-Specific Password

Table of Contents

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

Install

For Python 2.x and Python 3.x respectively:

pip install yagmail[all]
pip3 install yagmail[all]

As a side note, yagmail can now also be used to send emails from the command line.

Start a connection

yag = yagmail.SMTP('mygmailusername', 'mygmailpassword')

Note that this connection is reusable, closable and when it leaves scope it will clean up after itself in CPython.

As tilgovi points out in #39, SMTP does not automatically close in PyPy. The context manager with should be used in that case.

Usability

Defining some variables:

to = '[email protected]'
to2 = '[email protected]'
to3 = '[email protected]'
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 even to 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])

Recipients

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* aliases
yag.send(to = {to : 'Alias1'}) # Dictionary for emailaddress *with* aliases
yag.send(to = {to : 'Alias1', to2 : 'Alias2'}

Giving no to argument will send an email to yourself. In that sense, yagmail.SMTP().send() can already send an email. Be aware that if no explicit to = ... 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 using soft_email_validation==True (default).

Oauth2

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("[email protected]", oauth2_file="~/oauth2_creds.json")
yag.send(subject="Great!")

It will prompt for a google_client_id and a google_client_secret, when the file cannot be found. These variables can be obtained following the previous link.

After you provide them, a link will be shown in the terminal that you should followed to obtain a google_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.

Preventing OAuth authorization from expiring after 7 days

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 at https://console.cloud.google.com/apis/credentials/consent

Your OAuth client ID must be of type "Desktop". Check at https://console.cloud.google.com/apis/credentials

Magical contents

The contents 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 html e.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 and inline have been added.

  • raw ensures a string will not receive any "magic" (inlining, html, attaching)
  • inline will make an image appear in the text.

Attaching Files

There are multiple ways to attach files in the attachments parameter (in addition to magical contents parameter).

  1. 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']
)
  1. One can pass an instance of io.IOBase.
with open('path/to/attachment', 'rb') as f:
    yag.send(to=recipients,
             subject=email_subject,
             contents=contents,
             attachments=f
             )

In this example f is an instance of _io.BufferedReader a subclass of the abstract class io.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 all io.IOBase instances have the .name attribute in which case yagmail names the attachments attachment1, 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 or uploaded by a user in a web-application)

DKIM Support

To send emails with dkim signature, you need to install the package with all related packages.

pip install yagmail[all]
# or
pip install yagmail[dkim]

Usage:

from yagmail import SMTP
from yagmail.dkim import DKIM
from pathlib import Path

# load private key from file/secrets manager
private_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

Feedback

I'll try to respond to issues within 24 hours at Github.....

And please send me a line of feedback with SMTP().feedback('Great job!') :-)

Roadmap (and priorities)

  • Added possibility of Image
  • Optional SMTP arguments should go with **kwargs to my SMTP
  • CC/BCC (high)
  • Custom names (high)
  • Allow send to return a preview rather than to actually send
  • Just 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 type
  • Choose 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 install
  • Added feedback 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 emails
  • Logging count & mail capability (very low)
  • Add documentation to exception classes (low)
  • add raw and inline
  • 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)

Errors

  • 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 an email, and the value is an alias {'[email protected]': '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 externally https://www.google.com/settings/security/lesssecureapps

  • Make sure you have a working internet connection

  • If you get an ImportError try to install with sudo, see issue #13

Donate

If you like yagmail, feel free (no pun intended) to donate any amount you'd like :-)

PayPal

More Repositories

1

whereami

Uses WiFi signals 📶 and machine learning to predict where you are
Python
5,081
star
2

neural_complete

A neural network trained to help writing neural network code using autocomplete
Python
1,149
star
3

gittyleaks

💧 Find sensitive information for a git repo
Python
687
star
4

sky

🌅 next generation web crawling using machine intelligence
Python
321
star
5

contractions

Fixes contractions such as `you're` to `you are`
Python
301
star
6

access_points

Scan your WiFi and get access point information and signal quality
Python
181
star
7

textsearch

Find strings/words in text; convenience and C speed 🎆
Python
121
star
8

brightml

Convenient Machine-Learned Auto Brightness (Linux)
Python
119
star
9

shrynk

Using Machine Learning to learn how to Compress ⚡
Python
109
star
10

loco

Share localhost through SSH. Local/Remote port forwarding made safe and easy.
Python
105
star
11

cliche

Build a simple command-line interface from your functions 💻
Python
105
star
12

tok

Fast and customizable tokenization 🚤
Python
65
star
13

aserve

Easily mock an API ☕
Python
50
star
14

just

Just is a wrapper to automagically read/write a file based on extension
Python
48
star
15

xtoy

Automated Machine Learning: go from 'X' to 'y' without effort.
Python
47
star
16

spacy_api

Server/Client around Spacy to load spacy only once
Python
46
star
17

requests_viewer

View requests objects with style
Python
42
star
18

cant

For those who can't remember how to get a result
Python
33
star
19

aioyagmail

makes sending emails very easy by doing all the magic for you, asynchronously
Python
29
star
20

sysdm

Scripts as a service. Builds on systemd (for Linux)
Python
21
star
21

deep_eye2mouse

Move the mouse by your webcam + eyes
Python
20
star
22

reddit_ml_challenge

Reddit Machine Learning: Tagging Challenge
Python
19
star
23

inthenews.io

Get the latest and greatest in news (on Python)
CSS
19
star
24

crtime

Get creation time of files for any platform - no external dependencies ⏰
Python
16
star
25

natura

Find currencies / money talk in natural text
Python
15
star
26

rebrand

✨ Refactor your software using programming language independent, case-preserving string replacement 💄
Python
15
star
27

emacs-kooten-theme

Dark color theme by kootenpv
Emacs Lisp
14
star
28

justdb

Just a thread/process-safe, file-based, fast, database.
Python
8
star
29

fastlang

Fast Detection of Language without Dependencies
Python
7
star
30

quickpip

A template for creating a quick, maintainable and high quality pypi project
Python
7
star
31

xdb

Ambition: Single API for any database in Python
Python
6
star
32

nostalgia_chrome

Self tracking your online life!
Python
5
star
33

cnn_basics

NLP using CNN on Cornell Movie Ratings
Python
4
star
34

kootenpv.github.io

Pascal van Kooten's website hosted on github.io
CSS
3
star
35

gittraffic

Save your gittrafic data so it won't get lost!
Python
3
star
36

flymake-solidity

flymake for solidity, using flymake-easy: live feedback on writing solidity contracts
Emacs Lisp
3
star
37

ppm

Safe password manager
C
2
star
38

automl_presentation

Example code for the presentation "Automated Machine Learning"
Python
2
star
39

dot_access

Makes nested python objects easy to go through
Python
1
star
40

feedview

View a feed url with `feedview <URL>`
Python
1
star
41

PassMan

android app for ppm
C
1
star
42

mockle

Automatic Mocking by Pickles
Python
1
star
43

emoji-picker

Python
1
star