How to fix os.path error in windows machine? - python-3.x

I am trying to write a function that will send email with or without attachment based on the attachment file input but its failing with file not found error.
Here is my function
def sendEmail(TO, FROM, SUBJECT, BODY, *FILETOSEND):
"""Function to send email"""
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart("alternative")
msg["Subject"] = SUBJECT
msg["From"] = FROM
msg["To"] = TO
if FILETOSEND:
file_string = str(FILETOSEND)
finalFile = file_string[1 : len(file_string) - 2]
fp = open(finalFile)
attachment = MIMEText(fp.read())
fp.close()
attachment.add_header("Content-Disposition", "attachment", filename=fp)
msg.attach(attachment)
# Create the body of the message
text = BODY
part1 = MIMEText(text, "plain")
msg.attach(part1)
# Send the message via local SMTP server.
s = smtplib.SMTP("smtpservername")
s.sendmail(msg["From"], msg["To"], msg.as_string())
s.quit()
fileToSend = r"C:\Users\n123456\Desktop\DomainFolder\D1\NEWDATASET.txt"
sendEmail(TO, FROM, SUBJECT, BODY, *FILETOSEND)
I have passed all the arguments without filetosend and it worked but when passing with fileToSend its failing with OSerror
OSError: [Errno 22] Invalid argument:
"'C:\\\\Users\\\\n123456\\\\Desktop\\\\DomainFolder\\\\D1\\\\NEWDATASET.txt'"
I have tested with placing file in different directory and drive also with putting forward slash but still same issue.
As *FILETOSEND will return tuple I am trying string manipulation to make it correct path but no luck.
I am using windows 10 with Python 3.8. Seeking help.

Try to use path like this
open(r"C:\Users\n123456\Desktop\DomainFolder\D1\NEWDATASET.txt","r")

Related

Gmail account. Python 3.8 idle script. Error: smtplib.SMTPSenderRefused: (503, b'5.5.1 EHLO/HELO first

I'm doing an exercise on writing a module in python 3.8 idle (Mac) to send emails from my gmail account. It is giving me the error:
smtplib.SMTPSenderRefused: (503, b'5.5.1 EHLO/HELO first.
THE COMPLETE RUNNING RESULT:
= RESTART: /Users/mimikatz/Desktop/python/Python_note&exercise/send_email_gmail.py
person_name
Thank you for sharing!
Traceback (most recent call last):
File "/Users/mimikatz/Desktop/python/Python_note&exercise/send_email_gmail.py", line 58, in <module>
main()
File "/Users/mimikatz/Desktop/python/Python_note&exercise/send_email_gmail.py", line 51, in main
s.send_message(msg)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/smtplib.py", line 970, in send_message
return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/smtplib.py", line 871, in sendmail
raise SMTPSenderRefused(code, resp, from_addr)
smtplib.SMTPSenderRefused: (503, b'5.5.1 EHLO/HELO first. c18sm12642612wmk.18 - gsmtp', 'xxxxx#gmail.com')
The first two lines (person_name Thank you for sharing!) are from the 'message.txt' file.
MY CODE IS:
import smtplib
from string import Template
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from_addr = 'xxxxxg#gmail.com'
password = 'xxxxxxxxxxx'
smtp_server = 'smtp.gmail.com'
def get_contacts(self):
names = []
emails = []
with open(self, 'r', encoding='utf-8') as contacts_file:
for a_contact in contacts_file:
names.append(a_contact.split()[0])
emails.append(a_contact.split()[1])
return names, emails
def read_template(self):
with open(self, 'r', encoding='utf-8') as template_file:
template_file_content = template_file.read()
return Template(template_file_content)
def main():
names, emails = get_contacts('contacts.txt') # read contacts
message_template = read_template('message.txt')
# set up the SMTP server
s = smtplib.SMTP_SSL(smtp_server)
s.ehlo()
s.connect(smtp_server,465)
s.login(from_addr, password)
# For each contact, send the email:
for name, email in zip(names, emails):
msg = MIMEMultipart() # create a message
# add in the actual person name to the message template
message = message_template.substitute(person_name = name.title())
# prints out the message body for our sake
print(message)
# setup the parameters of the message
msg['From'] = from_addr
msg['To'] = email
msg['Subject'] = 'This is TEST'
# add in the message body
msg.attach(MIMEText(message, 'plain', 'utf-8'))
# send the message via the server set up earlier.
s.send_message(msg)
del msg
# Terminate the SMTP session and close the connection
s.quit()
if __name__ == '__main__':
main()
I saw this code in one of the questions asked here and it looked great to me as an example, so I wanted to try it out. But my problem turns out different than what the original one had. From the running result, it looks like the """print(message)""" command is successfully loaded. The problem occurs at """s.send_message(msg)""". I checked online several similar cases but couldn't find answer that suits this condition.
Really grateful to any help :)
Acorus
Problem solved thanks to Konrad
Swapping the .ehlo() with .connect()
& Changing the password to an authentication code generated from gmail setting 2-step verification app password. I used "mail" & "mac" to generate the verification code.
While I have never used Python to send emails, I've spent some time communicating with SMTP servers via Telnet.
You need to connect to the server first, then send EHLO, then authenticate and finally send the message. Your code seems to try sending EHLO before connecting to the server.
Try swapping those two lines.

RETR downloading zip File from ftp not writing

I am trying to donwload a huge zip file (~9Go zipped and ~130GO unzipped) from an FTP with python using the ftplib library but unfortunately when using the retrbinary method, it does create the file in my local diretory but it is not writing into the file. After a while the code runs, I get an timeout error. It used to work fine before, but when I tried to go deeper in the use of sockets by using this code it does not work anymore. Indeed, as the files I am trying to download are huge I want to have more control with the connection to prevent timeout error while downloading the files. I am not very familar with sockets so I may have misused it. I have been searching online but did not find any problems like this. (I tried with smaller files too for test but still have the same issues)
Here are the function that I tried but both have problems (method 1 is not writing to file, method 2 donwloads file but I can't unzip it)
import time
import socket
import ftplib
import threading
# To complete
filename = ''
local_folder = ''
ftp_folder = ''
host = ''
user = ''
mp = ''
# timeout error in method 1
def downloadFile_method_1(filename, local_folder, ftp_folder, host, user, mp):
try:
ftp = ftplib.FTP(host, user, mp, timeout=1600)
ftp.set_debuglevel(2)
except ftplib.error_perm as error:
print(error)
with open(local_folder + '/' + filename, "wb") as f:
ftp.retrbinary("RETR" + ftp_folder + '/' + filename, f.write)
# method 2 works to download zip file, but header error when unziping it
def downloadFile_method_2(filename, local_folder, ftp_folder, host, user, mp):
try:
ftp = ftplib.FTP(host, user, mp, timeout=1600)
ftp.set_debuglevel(2)
sock = ftp.transfercmd('RETR ' + ftp_folder + '/' + filename)
except ftplib.error_perm as error:
print(error)
def background():
f = open(local_folder + '/' + filename, 'wb')
while True:
block = sock.recv(1024*1024)
if not block:
break
f.write(block)
sock.close()
t = threading.Thread(target=background)
t.start()
while t.is_alive():
t.join(60)
ftp.voidcmd('NOOP')
def unzip_file(filename, local_folder):
local_filename = local_folder + '/' + filename
with ZipFile(local_filename, 'r') as zipObj:
zipObj.extractall(local_folder)
And the error I get for method 1:
ftplib.error_temp: 421 Timeout - try typing a little faster next time
And the error I get when I try to unzip after using method 2:
zipfile.BadZipFile: Bad magic number for file header
Alos, regarding this code If anyone could explain what this does concerning socketopt too would be helpful:
ftp.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
ftp.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 75)
ftp.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)
Thanks for your help.

Download one specific Outlook attachment from an email with multiple attachments

Working on a Python 3 win32com.client script that only searches for emails from a specific sender and downloads 1 out of multiple attachments.
The issue I have is that in the instances where an email has two attachments, it tries to download and rename both, which overwrites the first file that I want with the second attachment.
The file attachment has a specific filename but there is another attachment with a similar name.
So far I have:
import win32com.client
import os
mydesktop = os.path.expanduser('~') + '/Desktop/'
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
# Select main Inbox
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
sender = 'mysender#domain'
MyDailyfolder = mydesktop + 'My Daily Data/'
try:
for message in messages:
msg_date = message.SentOn.strftime('%Y-%m-%d')
try:
s = message.sender
s = str(s)
if s == sender:
for att in message.Attachments:
if "Dashboard2_dashboard2" in att.FileName: #<---This doesn't work.
outfile_name2 = 'MycustomName' + msg_date + '.csv'
outfile_path2 = MyDailyfolder + outfile_name2
if not os.path.exists(MyDailyfolder): os.makedirs(MyDailyfolder)
# save file
att.SaveASFile(outfile_path2)
print('Saved file:', outfile_name2)
except:
x=1
except:
x=1
The result downloads all of the attached files in an email to a new folder on my desktop but it overwrites each copy. I'm trying to select only the attachment that contains "Dashboard2dashboard2" on it. I think I have to use "for part in msg.walk():" but have never used that command before. Any ideas?
Ah the issue is a typo in my filename search. An extra underscore. Added an extra print in each step to make sure each part is valid.
sender = 'mysender#domain'
MyDailyfolder = mydesktop + 'My Daily Data/'
try:
for message in messages:
msg_date = message.SentOn.strftime('%Y-%m-%d')
try:
s = message.sender
s = str(s)
if s == sender:
print('Sender:' , message.sender)
for att in message.Attachments:
if "dashboard_2.csv" in att.FileName:
outfile_name = msg_date + att.FileName
print ('Match search confirmed')
# Backup test, print filename and watch spelling
print (att.FileName)
# Create a folder and copy/paste attachment there
outfile_path = MyDailyfolder + outfile_name
if not os.path.exists(MyDailyfolder): os.makedirs(MyDailyfolder)
# save file
att.SaveASFile(outfile_path)
except:
pass
except:
pass

Python's Email Message library output not getting accepted by Outlook 365 when i have a named attachments from

I've created a sample function to test sending emails with an attached html file, which i intend to use for reporting on automated test runs in the future (replacing an existing external powershell script). Note that I'm attaching the html file, not using the html as inline text in the body. I'm using our company's mailgun smtp account service to send the email.
I seem to have an issue with Outlook 365 (web hosted - uses the outlook.office.com domain) either rejecting or blocking the sent email, but interestingly the same email is received and accepted by my personal hotmail address (outlook.live.com domain). I've found Outlook 365 blocks or does not accept the email when I attempt to name the file in the email message object. But if I don't name it, it will come through (with a default name of "ATT00001.htm" ).
My code for this is below but they key line seems to be
msg.add_attachment(open_file.read(), maintype='text', subtype='html', filename=filename)
If I drop the filename key it works (but with a default assigned filename) e.g.
msg.add_attachment(open_file.read(), maintype='text', subtype='html')
I have a suspicion there is something in the attachment's header or Content-disposition that Outlook 365 doesn't agree with, but i'm not sure what it is or how to work around.
I'm using the following (Python 3.6.5, on Windows 10 machine, smtplib and email.message seem to be built in)
Here is the code:
import smtplib
from email.message import EmailMessage
import os
def send_mail():
MAILGUN_SMTP_LOGIN = "<my company's mailgun login>"
MAILGUN_SMTP_PASSWORD = "<my company's mailgun password>"
fromaddr = "muppet#sharklasers.com" # the from address seems to be inconsequential
toaddr = ['me#mycompanysdomainusingoffice365.com.au', 'me#hotmail.com']
msg = EmailMessage()
msg.preamble = 'This is preamble. Not sure where it should show in the email'
msg['From'] = fromaddr
msg['To'] = ', '.join(toaddr)
msg['Subject'] = 'Testing attached html results send'
msg.set_content(""" This is a test of attached html """)
filename = 'api_automatedtests_20180903_1341.html'
filepath = os.path.abspath('D:/work/temp/api_automatedtests_20180903_1341.html')
open_file = open(filepath, "rb")
# msg.make_mixed()
msg.add_attachment(open_file.read(), maintype='text', subtype='html', filename=filename)
# msg.add_attachment(open_file.read(), maintype='text', subtype='html')
server = smtplib.SMTP(host="smtp.mailgun.org", port=587)
server.ehlo()
server.starttls()
server.login(MAILGUN_SMTP_LOGIN, MAILGUN_SMTP_PASSWORD)
server.set_debuglevel(1)
server.send_message(msg)
server.quit()
if __name__ == "__main__":
send_mail()
What I've tried
Tried sending with the same code using a textfile (with appropriate types). e.g.
msg.add_attachment(open_file.read(), maintype='text', subtype='plain', filename=filename)
Result: This works as expected (comes through with the given name - the filename is a string variable e.g. testfile.txt)
adding msg.make_mixed() to make sure it is identified as a multipart message. Result: No effect
Turning on the smtp debug level 1, Result: Mailgun says that everything has worked fine (and the messages do appear as expected in my hotmail account)
Not using the filename key in the msg.add_attachment call.
Result: This works the attachment comes through at ATT00001.htm
Interestingly the default name is *.htm while the filename I'm trying to use is *.html
Tried using a filename with *.htm and a subtype of 'htm' (instead of html)
Result: Same as for html (received on hotmail but not on outlook 365)
Tried using the generic types of maintype=''application', subtype='octet-stream'.
e.g. msg.add_attachment(open_file.read(), maintype='application', subtype='octet-stream', filename=filename)
Result: Same as for html (received on hotmail but not on outlook 365)
Tried using mimetypes.guess as shown in this link
https://docs.python.org/3.6/library/email.examples.html
ctype, encoding = mimetypes.guess_type(path)
if ctype is None or encoding is not None:
# No guess could be made, or the file is encoded (compressed), so
# use a generic bag-of-bits type.
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
with open(path, 'rb') as fp:
msg.add_attachment(fp.read(),
maintype=maintype,
subtype=subtype,
filename=filename)
Result: It's determined as maintype='text', subtype='html' and I get the same result as with my original code (ie arrives in hotmail but blocked by 365).
Checking my spam and clutter folders - was not there
Any suggestions on why the use of filename would be breaking it?
Update
After sending to a other email addresses with various providers I discovered:
1) muppet#sharklasers.com was not a trusted sender (can change this)
2) I discovered the attachment was being flagged as unsafe. The html file comes from pytest's html report with the single file option. It contains javascript for row expanders. Gmail warns the attachment may not be safe (office 365 just straight out blocks the email altogether).
Not sure how to work around 2). I can email the same file to myself between outlook 365 and gmail and vice versa and the file doesn't get blocked. It only get's blocked when I use the above script using python's libraries and Mailgun SMTP. I suspect there is something I need to change in the email header to get around this. But I don't know what.
There seems to be some connection between trying to add the filename and the attachment being marked as unsafe
Okay I figured it out. The problem was the content-type needed to include "name=filename" in it's value.
Also I needed to use maintype='multipart', subtype='mixed'.
I have 2 solutions.
solution 1
import smtplib
from email.message import EmailMessage
import os
def send_mail(body_text, fromaddr, recipient_list, smtp_login, smtp_pass, file_path):
msg = EmailMessage()
msg.preamble = 'This is preamble. Not sure where it should show'
msg['From'] = fromaddr
msg['To'] = ', '.join(recipient_list)
msg['Subject'] = 'API Testing results'
msg.set_content(body_text)
filename = os.path.basename(file_path)
open_file = open(file_path, "rb")
msg.add_attachment(open_file.read(), maintype='multipart', subtype='mixed; name=%s' % filename, filename=filename)
server = smtplib.SMTP(host="smtp.mailgun.org", port=587)
server.ehlo()
server.starttls()
server.login(smtp_login, smtp_pass)
server.send_message(msg)
server.quit()
if __name__ == "__main__":
smtp_login = "<my smtp login>"
smtp_pass = "<my smtp password>"
recipient_list = ['user1#mycompany.com.au', 'user2#mycompany.com.au']
file_path = os.path.abspath('D:/work/temp/api_automatedtests_20180903_1341.html')
body_text = "test results for 03/09/2018 "
fromaddr = 'autotesting#mycompany.com.au'
send_mail(body_text=body_text, recipient_list=recipient_list, smtp_login=smtp_login, smtp_pass=smtp_pass,
file_path=file_path)
solution 2 (according to the documentation using the email.mime libraries is a legacy solution and the EmailMessage method is supposed to be used in preference.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
def send_mail(body_text, fromaddr, recipient_list, smtp_login, smtp_pass, file_path):
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = ', '.join(recipient_list)
msg['Subject'] = "Sending API test results"
msg.attach(MIMEText(body_text, 'plain'))
filename = os.path.basename(file_path)
attachment = open(file_path, "rb")
part = MIMEBase('multipart', 'mixed; name=%s' % filename)
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
server = smtplib.SMTP(host="smtp.mailgun.org", port=587)
server.starttls()
server.login(smtp_login, smtp_pass)
text = msg.as_string()
server.set_debuglevel(1)
server.sendmail(fromaddr, recipient_list, text)
server.quit()
if __name__ == '__main__':
smtp_login = "<my smtp login>"
smtp_pass = "<my smtp password>"
recipient_list = ['user1#mycompany.com.au', 'user2#mycompany.com.au']
file_path = os.path.abspath('D:/work/temp/api_automatedtests_20180903_1341.html')
body_text = " Api test results for 03/09/2018 "
fromaddr = "autotest#mycompany.com.au"
send_mail(body_text=body_text, fromaddr=fromaddr, recipient_list=recipient_list, smtp_login=smtp_login, smtp_pass=smtp_pass,
file_path=file_path)

ASCII encoding error when sending an email in Python3?

I have a program in Python3 which sends mails. It works perfectly on several computers, but there is one in which not. I tried and this one only works when there are not any special characters like 'ñ' or 'á', 'é', etc...
I got the next error:
'ascii' codec can't encode character '\whatever' in position x: ordinal not in range(128)
And this is my code:
html = '<h1>niñería</h1>'
text = 'niñería'
mail = MIMEMultipart('alternative')
mail['From'] = 'my_account#gmail.com'
mail['To'] = 'destiny#gmail.com'
mail['Cc'] = ''
mail['Subject'] = 'My subject'
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
# Attach parts into message container. According to RFC 2046, the last
# part of a multipart message, in this case the HTML message, is best
# and preferred.
mail.attach(part1)
mail.attach(part2)
msg_full = mail.as_string()
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login('my_account#gmail.com', 'my_password')
server.sendmail('my_account#gmail.com', ['destiny#gmail.com'], msg_full)
server.quit()
Is there any magic line like .encode('utf-8') or .decode('utf-8') which could make my email content recognised?
OK, solved. I only had to change the line:
msg_full = mail.as_string()
And write instead:
msg_full = mail.as_string().encode()

Resources