Sending Email Using Python and Gmail


23 posts
by ShadowEEngineer » Tue Jan 29, 2013 10:22 pm
Hello,

I'm new to the RPi world, and as my first "dive" I chose to follow a couple of examples of sending emails through the console (running Raspberrian). More specifically, I've tried the example in the educational manual (p. 116, http://downloads.raspberrypi.org/Raspberry_Pi_Education_Manual.pdf) and here http://elinux.org/RPi_Email_IP_On_Boot_Debian. The latter is more relevant to what I'm trying to do, so I'll attach the code:

Code: Select all
import subprocess
import smtplib
import socket
from email.mime.text import MIMEText
import datetime
# Change to your own account information
to = 'me@example.com'
gmail_user = 'test@gmail.com'
gmail_password = 'yourpassword'
smtpserver = smtplib.SMTP('smtp.gmail.com', 587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo
smtpserver.login(gmail_user, gmail_password)
today = datetime.date.today()
# Very Linux Specific
arg='ip route list'
p=subprocess.Popen(arg,shell=True,stdout=subprocess.PIPE)
data = p.communicate()
split_data = data[0].split()
ipaddr = split_data[split_data.index('src')+1]
my_ip = 'Your ip is %s' %  ipaddr
msg = MIMEText(my_ip)
msg['Subject'] = 'IP For RaspberryPi on %s' % today.strftime('%b %d %Y')
msg['From'] = gmail_user
msg['To'] = to
smtpserver.sendmail(gmail_user, [to], msg.as_string())
smtpserver.quit()


Now, when I try to run either one on IDLE, I get syntax error such as "% ipaddr." Then I remove the the code that gives errors (mainly the ip relevant things), and when it complies I get an error regarding the smtplib and MIMEText. Lastly, When I only type "import smtplib" in the python shell, it returns an error.

How can I fix this?

Thank you
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by alexeames » Tue Jan 29, 2013 11:37 pm
Can you try it without IDLE? It sometimes does strange things. Try running it in the command line with
Code: Select all
python progname.py


I confess I haven't looked closely at the code, but the email address and password looked generic.

I wrote a python program back in the summer to send an email using googlemail every time you log in. (Although it won't run every time you log in unless you set it up to do that).

If you want to see the code I can post a link.
My Pi uses 2 watts - what what? ---- HiRes early production Pi photos RS Front Back | Farnell Front Back
User avatar
Posts: 2018
Joined: Sat Mar 03, 2012 11:57 am
Location: UK
by ShadowEEngineer » Tue Jan 29, 2013 11:41 pm
Hi Alex,

Thanks for your reply. Obviously I changed the user/pass values. The main issue that it doesnt even compile in its general form.

Regardless, I'll try running it from the command line and post the results here in about 2hrs, once I'll finish work....
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by bgreat » Wed Jan 30, 2013 12:53 am
First thing. What Linux distribution are you using?

Doing a quick review of the code, I see a syntax error here:
Code: Select all
...
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo
...

The third line above should be:
Code: Select all
smtpserver.ehlo()

The missing '()' will result in an error.

With the correction, your script runs for me on my Raspbian Rasberry Pi.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by bgreat » Wed Jan 30, 2013 1:01 am
ShadowEEngineer wrote:Lastly, When I only type "import smtplib" in the python shell, it returns an error.

This indicates that you may have an installation problem. If you are not running the most recent Raspbian image, I would recommend you update your installation and try your script again.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by ShadowEEngineer » Wed Jan 30, 2013 2:32 am
Hi guys,

Thanks again for the posts, I just attempted all of the suggestions and:
1. When I type import smtplib in the shell, i get an error (see in the attached imaged, top left).
2. When I added the missing (), it still didn't run, I guess the main issue is rooted deeper than that.
3. Upon compiling I get an error of invalid syntax (see top picture on the right, the red pixel (of '%') in the middle of it.
4. The same error comes when I try to run it from the command line (bottom left picture).

The image I've loaded on the SD card is of 2012-12-16-wheezy-raspbian.

Should I re-image the OS?
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by ShadowEEngineer » Wed Jan 30, 2013 2:40 am
Sorry forgot the attachment....
Attachments
python_output2.jpg
python_output2.jpg (31.66 KiB) Viewed 3590 times
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by ShadowEEngineer » Wed Jan 30, 2013 2:42 am
one more.sorry for the multiple posts...
Attachments
python_output.jpg
python_output.jpg (55.28 KiB) Viewed 3589 times
python_output1.jpg
python_output1.jpg (59.57 KiB) Viewed 3589 times
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by bgreat » Wed Jan 30, 2013 3:00 am
First, you should update your Raspbian installation by running the following commands on your Raspberry Pi:
Code: Select all
sudo apt-get update
sudo apt-get -y upgrade

This should solve your import problem. Until that is corrected, the program can not work.

Second. What editor did you use to create 'email_startup.py' ? The error indicates the file is not a plain text file. You need to use an editor that writes the file with no special formatting embedded. I use 'vi' from the command line. There is also 'nano' for use from a command prompt. If you are running from the LXDE desktop, the IDLE editor will save as plain text. If you are using a text editor, then you somehow have a special character in the line that you need to replace. I usually delete the line and retype if this occurs.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by ShadowEEngineer » Wed Jan 30, 2013 3:43 am
Hi Bill,

I've done the update and the update, but I keep getting the same error when entering import smtplib in the shell :
raceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
import smtplib
File "/usr/lib/python2.7/smtplib.py", line 46, in <module>
import email.utils
File "email.py", line 2, in <module>
from email.mime.text import MIMEText
ImportError: No module named mime.text

I think I'll just format the SD and reload the image.....
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by malakai » Wed Jan 30, 2013 3:50 am
Not sure but do you have email.py in the same folder as the code your running if so won't python pick that up first invalidating the email.py that your trying to import?
http://www.raspians.com - always looking for content feel free to ask to have it posted. Or sign up and message me to become a contributor to the site. Raspians is not affiliated with the Raspberry Pi Foundation. (RPi's + You = Raspians)
User avatar
Posts: 1383
Joined: Sat Sep 15, 2012 10:35 am
by bgreat » Wed Jan 30, 2013 4:00 am
malakai wrote:Not sure but do you have email.py in the same folder as the code your running if so won't python pick that up first invalidating the email.py that your trying to import?

Good catch. That would definitely cause the error.

Enjoy!
Bill
User avatar
Posts: 235
Joined: Mon Jan 23, 2012 2:09 pm
by alexeames » Wed Jan 30, 2013 8:33 am
Another thing I found out the hard way is that you cannot use the filename email.py :lol:
My Pi uses 2 watts - what what? ---- HiRes early production Pi photos RS Front Back | Farnell Front Back
User avatar
Posts: 2018
Joined: Sat Mar 03, 2012 11:57 am
Location: UK
by ShadowEEngineer » Wed Jan 30, 2013 10:54 pm
Thank you guys, it work like a charm now. I didn't try vi editor yet, so I just removed the invalid characters and it worked. :)
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by WildDude92 » Thu Apr 04, 2013 12:04 am
Using the filename email.pyc was my problem too. I also had to rm email.pyc
Posts: 1
Joined: Thu Apr 04, 2013 12:02 am
by ShadowEEngineer » Thu Apr 04, 2013 8:58 am
Yeah, completely new to python....
I had to start from scratch again due to sdhc malfunction, let me ask you a question about my new script. It send email fine with n attachment , but I can't get the multiple recipients to work. It'll be very helpful if you can take a look.

Code: Select all
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
import os

gmail_user = "shadow@gmail.com"
gmail_pwd = "123456"

def mail(to, subject, text, attach):
   msg = MIMEMultipart()
   msg['From'] = gmail_user
   msg['To'] = ', '.join(to)
   msg['Subject'] = subject

   msg.attach(MIMEText(text))

   part = MIMEBase('application', 'octet-stream')
   part.set_payload(open(attach, 'rb').read())
   Encoders.encode_base64(part)
   part.add_header('Content-Disposition',
           'attachment; filename="%s"' % os.path.basename(attach))
   msg.attach(part)

   mailServer = smtplib.SMTP("smtp.gmail.com", 587)
   mailServer.ehlo()
   mailServer.starttls()
   mailServer.ehlo()
   mailServer.login(gmail_user, gmail_pwd)
   mailServer.sendmail(gmail_user, to, msg.as_string())
   # Should be mailServer.quit(), but that crashes...
   mailServer.close()

mail("abc@gmail.com,defg@gmail.com",
   "New Scripts",
   "You have a new zip",
   "/home/pi/scripts.tar")



Thank you in advance.
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by -rst- » Thu Apr 04, 2013 2:23 pm
You might want to debug what happens to your recipient list...
Code: Select all
>>> to = "abc@gmail.com,defg@gmail.com"
>>> ', '.join(to)
'a, b, c, @, g, m, a, i, l, ., c, o, m, ,, d, e, f, g, @, g, m, a, i, l, ., c, o, m'
>>>
...does not look what was meant... maybe try
Code: Select all
to.split(',')
if the plain string is not accepted...
http://raspberrycompote.blogspot.com/ - Low-level graphics and 'Coding Gold Dust'
Posts: 893
Joined: Thu Nov 01, 2012 12:12 pm
Location: Dublin, Ireland
by rurwin » Thu Apr 04, 2013 2:36 pm
I disagree, but you made me see the bug.

Code: Select all
mail("abc@gmail.com,defg@gmail.com",
   "New Scripts",
   "You have a new zip",
   "/home/pi/scripts.tar")


Should be:
Code: Select all
mail(["abc@gmail.com", "defg@gmail.com"],
   "New Scripts",
   "You have a new zip",
   "/home/pi/scripts.tar")


To should be a list of email addresses, because that is what is required for the ultimate mailServer.sendmail(gmail_user, to, msg.as_string()) call.

And in that case, join() will work correctly.
User avatar
Moderator
Moderator
Posts: 2890
Joined: Mon Jan 09, 2012 3:16 pm
by -rst- » Thu Apr 04, 2013 3:33 pm
So the 'to' parameter (of the sendmail function) is not a list but a string where multiple addresses are separated by ", " (a comma and a space) and the original plain string would work by adding just the space...

Code: Select all
mail("abc@gmail.com, defg@gmail.com",
   "New Scripts",
   "You have a new zip",
   "/home/pi/scripts.tar")

and
Code: Select all
def mail(to, subject, text, attach):
   msg = MIMEMultipart()
   msg['From'] = gmail_user
   msg['To'] = to
   ...
Last edited by -rst- on Thu Apr 04, 2013 3:43 pm, edited 1 time in total.
http://raspberrycompote.blogspot.com/ - Low-level graphics and 'Coding Gold Dust'
Posts: 893
Joined: Thu Nov 01, 2012 12:12 pm
Location: Dublin, Ireland
by rurwin » Thu Apr 04, 2013 3:43 pm
Python Standard Library Reference wrote: SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options])

Send mail. The required arguments are an RFC 822 from-address string, a list of RFC 822 to-address strings (a bare string will be treated as a list with 1 address), and a message string.


The contents of the "To:" header line must be a comma-delimited string, but that is distinct from the list in sendmail().

The To: header line is what is included in the email, and can be totally different from what goes on the envelope -- the sendmail() to_addrs parameter. For example the To: line can be just the name of a mailing list, and the to_addrs parameter can be the list of people in the mailing list. That way none of the recipients see any of the other email addresses.
User avatar
Moderator
Moderator
Posts: 2890
Joined: Mon Jan 09, 2012 3:16 pm
by -rst- » Thu Apr 04, 2013 4:12 pm
Ah well, so there's two different uses for the recipient addresses: one a list and one a string ...so I was close with my suggestion to use 'split()' but not quite where it should have been:
Code: Select all
def mail(to, subject, text, attach):
   msg = MIMEMultipart()
   msg['From'] = gmail_user
   msg['To'] = to
   ...
   mailServer.sendmail(gmail_user, to.split(","), msg.as_string())
...

mail("abc@gmail.com,defg@gmail.com",
   "New Scripts",
   "You have a new zip",
   "/home/pi/scripts.tar")
...would have worked. Obviously rurwin's solution suits the original mail() function better.

Always good to write
maybe try
;)
Last edited by -rst- on Fri Apr 05, 2013 9:30 am, edited 1 time in total.
http://raspberrycompote.blogspot.com/ - Low-level graphics and 'Coding Gold Dust'
Posts: 893
Joined: Thu Nov 01, 2012 12:12 pm
Location: Dublin, Ireland
by ShadowEEngineer » Thu Apr 04, 2013 4:50 pm
Hi Guys,

Thanks for the very informative answers. I wasn't aware of the differences between the To and to. Additionally, I see it's beneficial to use the command line python shell for debugging.

I've added square brackets as suggested above when the function is called, since it looked like the quickest fix and it worked.

Thank you very much again,
Posts: 24
Joined: Mon Jan 28, 2013 6:45 pm
by -rst- » Fri Apr 05, 2013 9:42 am
ShadowEEngineer wrote:...Additionally, I see it's beneficial to use the command line python shell for debugging...


The command line interpreter is very handy for testing out individual bits of code ...but may lead to blindness over the bigger picture (as happened to me there).

Also good to remember that using 'intermediary variables' and the print function is a viable way to debug - something along:

Code: Select all
def mail(to, subject, text, attach):
    print "mail(): to =" + to
    msg = MIMEMultipart()
    msg['From'] = gmail_user
    msg_to = ', '.join(to)
    print "msg_to=" + msg_to
    msg['To'] = msg_to
    ...
http://raspberrycompote.blogspot.com/ - Low-level graphics and 'Coding Gold Dust'
Posts: 893
Joined: Thu Nov 01, 2012 12:12 pm
Location: Dublin, Ireland