1

I am working on learning how to send an email via python. I have something set up using EmailMessage, but I am unsure as to what my error is trying to tell me in the set_content line.

Code:

Email_Address = os.environ.get('Email_User') #email address variable
Email_Password = os.environ.get('Email_Pass') #email password variable
msg = EmailMessage() #making an email class object
msg['Subject'] = 'That_thing' #subject line
msg['From'] = Email_Address #from address line
msg['To'] = Email_Address #to address line. can be made into a list variable for multiple receivers
msg.set_content('Did you get that thing I sent you?') # email body
with open('that_thing.jpeg', 'rb') as f: #rb==read bytes
    file_data = f.read()
    file_type = imghdr.what(f.name)
    file_name = f.name
msg.add_attachment(filedata=file_data, maintype='image', subtype=file_type, filename=file_name)
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp: #using smtp on secure socket layer to log in and send email
    smtp.login(Email_Address, Email_Password) #user login information
    smtp.send_message(msg) #sending the actual email

Error:

TypeError                                 Traceback (most recent call last)
<ipython-input-6-3336c41d40e5> in <module>
     13     file_type=imghdr.what(f.name)
     14     file_name=f.name
---> 15 msg.add_attachment(filedata=file_data,maintype='image',subtype=file_type,filename=file_name)
     16 with smtplib.SMTP_SSL('smtp.gmail.com',465) as smtp: #using smtp on secure socket layer to log in and send email
     17     smtp.login(Email_Address,Email_Password) #user login information

~\Anaconda3\lib\email\message.py in add_attachment(self, *args, **kw)
   1145 
   1146     def add_attachment(self, *args, **kw):
-> 1147         self._add_multipart('mixed', *args, _disp='attachment', **kw)
   1148 
   1149     def clear(self):

~\Anaconda3\lib\email\message.py in _add_multipart(self, _subtype, _disp, *args, **kw)
   1133             getattr(self, 'make_' + _subtype)()
   1134         part = type(self)(policy=self.policy)
-> 1135         part.set_content(*args, **kw)
   1136         if _disp and 'content-disposition' not in part:
   1137             part['Content-Disposition'] = _disp

~\Anaconda3\lib\email\message.py in set_content(self, *args, **kw)
   1160 
   1161     def set_content(self, *args, **kw):
-> 1162         super().set_content(*args, **kw)
   1163         if 'MIME-Version' not in self:
   1164             self['MIME-Version'] = '1.0'

~\Anaconda3\lib\email\message.py in set_content(self, content_manager, *args, **kw)
   1090         if content_manager is None:
   1091             content_manager = self.policy.content_manager
-> 1092         content_manager.set_content(self, *args, **kw)
   1093 
   1094     def _make_multipart(self, subtype, disallowed_subtypes, boundary):

TypeError: set_content() missing 1 required positional argument: 'obj'

The video I was watching has it the same way I do, and they do not get an error.

Any help is appreciated.

Update: first issue resolved. I had my .py as Email making it think it was importing itself. now to figure out attachments.

Update:

I got it to work. After searching through the error I learned that imghdr has a known issue. Found imghdr / python - Can't detec type of some images (image extension) which has a patch to fix the issue.

Shaido
  • 27,497
  • 23
  • 70
  • 73
  • Did you try sending an Email without attachment? Does that work? – VirtualScooter Feb 22 '21 at 23:08
  • that gives a nonetype error: 'code' AttributeError: 'NoneType' object has no attribute 'encode' 'code' and a SMTPResponseException: (334, b'UGFzc3dvcmQ6') – Jake Nelson Feb 22 '21 at 23:12
  • It might be that your environment variables are not coming over, too. I ran the example code in my answer with a regular string for `sender` and `addressee`, and an `input()` for the password. It runs fine. – VirtualScooter Feb 23 '21 at 03:12

2 Answers2

0

From imghdr / python - Can't detec type of some images (image extension) '''

# Monkeypatch bug in imagehdr
from imghdr import tests

def test_jpeg1(h, f):
    """JPEG data in JFIF format"""
    if b'JFIF' in h[:23]:
        return 'jpeg'


JPEG_MARK = b'\xff\xd8\xff\xdb\x00C\x00\x08\x06\x06' \
            b'\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f'

def test_jpeg2(h, f):
    """JPEG with small header"""
    if len(h) >= 32 and 67 == h[5] and h[:32] == JPEG_MARK:
        return 'jpeg'


def test_jpeg3(h, f):
    """JPEG data in JFIF or Exif format"""
    if h[6:10] in (b'JFIF', b'Exif') or h[:2] == b'\xff\xd8':
        return 'jpeg'

tests.append(test_jpeg1)
tests.append(test_jpeg2)
tests.append(test_jpeg3)

'''

  • Yes, but this does not help answer your question. It will only confuse other readers. I vote you delete this answer. – VirtualScooter Feb 23 '21 at 03:14
  • I stand corrected. Good find @JakeNelson.Note that I did not patch the imghdr library on my system. But I used a PNG image to test. – VirtualScooter Feb 23 '21 at 03:19
  • A question on your example. If I am correct this is pushing the password in plain text via ssl, what does you del function do with it after? I've been finding about how to deal with the password, I know you can use a temp one but I've been trying to find a way to make this work on a weekly basis, and I've also been looking into tls instead. I would rather use pki or at least pass a hash instead. – Jake Nelson Feb 24 '21 at 05:23
0

See [https://docs.python.org/3.9/library/email.examples.html], third example. It turns out the only 'fatal' error here is using a named argument for the file contents (msg.add_attachment(filedata=file_data); there is no named argument filedata for this method. See example code below for suggested improvement:

import email.message as em
import imghdr
import smtplib

sender = os.environ.get('Email_User')
addressee = sender
smtpaddr = "smtp.gmail.com"
passwd = os.environ.get('Email_Pass')
port = 465
if __name__ == "__main__":
    msg = em.EmailMessage()
    msg['Subject'] = 'That_thing'
    msg['From'] = sender
    msg['To'] = addressee
    msg.set_content('Did you get that thing I sent you?')
    with open('small picture.png', 'rb') as f:
        file_data = f.read()
        file_type = imghdr.what(None, file_data)
        file_name = f.name
    msg.add_attachment(file_data,
                       maintype='image',
                       subtype=file_type)
    with smtplib.SMTP_SSL(smtpaddr, port) as smtp:
        smtp.login(sender, passwd)
        del passwd
        smtp.send_message(msg)
    print(f"Email sent to {addressee}")
VirtualScooter
  • 1,792
  • 3
  • 18
  • 28