Pagina 1 di 1

[Risolto][Python3][sendmail] encoding/decoding di email

Inviato: sabato 18 marzo 2017, 19:31
da rai
Un saluto a tutti.

Uno script (peraltro funzionante senza errori) invia una mail e *alcuni* destinatari non ne leggono i caratteri nonASCII. Per dire: gli stessi caratteri diventano leggibili se quel destinatario inoltra quella stessa mail su Gmail.
La stringa che costituisce il corpo della mail viene estratta da un file di appoggio creato localmente. Questa è la mia localizzazione

Codice: Seleziona tutto

$ locale
LANG=it_IT.UTF-8
LANGUAGE=it:fr:en
LC_CTYPE="it_IT.UTF-8"
LC_NUMERIC=it_IT.UTF-8
LC_TIME=it_IT.UTF-8
LC_COLLATE="it_IT.UTF-8"
LC_MONETARY=it_IT.UTF-8
LC_MESSAGES="it_IT.UTF-8"
LC_PAPER=it_IT.UTF-8
LC_NAME=it_IT.UTF-8
LC_ADDRESS=it_IT.UTF-8
LC_TELEPHONE=it_IT.UTF-8
LC_MEASUREMENT=it_IT.UTF-8
LC_IDENTIFICATION=it_IT.UTF-8
LC_ALL=
Penso che sia un problema mio e non (solo) dei destinatari perché se eseguo l' invio con uno script bash, a partire dallo stesso file di testo, i caratteri si leggono tutti, e anche sui destinatari `problematici'. Quindi deve esserci un modo per evitare questo fastidio ma non mi raccapezzo.

posto il frammento che ritengo utile, sfoltito dagli import e da tutti i controlli di errori ed eccezioni

Codice: Seleziona tutto

[ . . . ]
ilTXT = "/tmp/{}.txt".format(CognomeNome) # il file di testo (creato con libreoffice)
with open(ilTXT, "r") as f:
    testo = f.read()
for regex in LISTAREGEX:
    match = re.search(regex, testo, re.DOTALL)
    if 'oggetto1' in regex:
        SUBJECT = match.group(0).strip()
    elif 'contatti' in regex:
        TO = match.group(0).strip()
    elif 'messaggio1' in regex:
        BODY = match.group(0).strip()

#
#   costruisce le mail   #
#¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
message = """\
From: {}
To: {}
Subject: {}

{}
""".format(USER, TO, SUBJECT, BODY)

#
#   invia le mail   #
#¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
listaDestinatari = TO.split(',')

s = smtplib.SMTP_SSL(smtpSERVER)invio
s.login(USER, password)
s.sendmail(USER, listaDestinatari, message.encode('utf-8')) # <------- ecco il punto
s.quit
[ . . . ]
ma se servisse posto tutto il malloppo, l'ho sfoltito sperando di invogliare di più alla lettura i volenterosi
Grazie

Re: [Python3] encoding/decoding di caratteri

Inviato: sabato 18 marzo 2017, 22:28
da crap0101
ipotizzo che il problema sia appunto la codifica in utf-8... certi "visualizzatori" probabilmente sono abbastanza "furbi" da riconoscere che non è testo ascii e quindi lo decodificano correttamente, mentre altri non lo fanno e - in mancanza di specifiche - lo trattano come "plain text".
btw, nella doc del metodo sendmail() si dice appunto che il parametro "msg" dev'essere:
msg may be a string containing characters in the ASCII range, or a byte string
per cui è possibile che testo codificato diversamente dia problemi.

In un prog che mi pare ti avevo già linkato avevo fato diversamente, creando una classe per i messaggi esclusivamente in ascii e altre per quelli più "complessi", di cui ti riporto un estratto:

Codice: Seleziona tutto

class PlainMsg(MailMessage):
    """Plain text mail object."""
    def __init__(self, sender, receiver, subject, text):
        super(PlainMsg, self).__init__(
            sender, receiver, subject, text, None);

    def get_message(self, receiver=None):
        receiver = receiver if receiver is not None else self.receiver
        _time = mmutils.mail_format_time()
        return ("From: %s\r\nTo: %s\r\nSubject: %s\r\n"
                "Date: %s\r\nX-Mailer: %s\r\n\r\n%s"
                % (self.sender, receiver, self.subject,
                   _time, self.xmailer, self.text))

class MimeMsg(MailMessage):
    """Plain text mail object."""
    def __init__(self, sender, receiver, subject, text, ttype, attachments):
        super(MimeMsg, self).__init__(
            sender, receiver, subject, text, attachments)
        self.text_type = ttype
        self.build()

    def build(self):
        if not self.attachments:
            if self.text_type == 'html':
                self.msg = MIMENonMultipart('text', 'html')
            else:
                self.msg = MIMENonMultipart('text', 'plain', charset='utf-8')
            self.msg.set_payload(self.text)
            self.msg['boundary'] = self.delimiter
        else:
            self.msg = MIMEMultipart(boundary=self.delimiter)
            if self.text_type == 'html':
                self.msg.attach(MIMEText(self.text, 'html'))
            else:

in cui come vedi uso il modulo email.mime che semplifica un po' le cose... e che vedo nella stdlib di py3 è stato almeno in parte sostituito da email.contentmanager

Re: [Python3] encoding/decoding di caratteri

Inviato: domenica 19 marzo 2017, 12:53
da rai
Grazie crap0101, ho fatto tesoro del programma che hai condiviso in questa discussione, tra l'altro anche applicando la classe email.mime.text.MIMEText() che determina Content-Type e charset in modo trasparente all'utente.
Ma l'ho usata solo per costruire mail con allegati. Per mail non-multipart mi era sembrato overkill usare il modulo email per costruire il messaggio e invece ...

Comunque la doc di sendmail, che tu citi, mi sembra migliorabile: dice di fornire il msg come bytes, che è appunto quello che fa il mio script, ma non avvisa ( se non fra le righe ) della possibile mis-decodifica da parte dei destinatari.

Re: [Python3] encoding/decoding di caratteri

Inviato: lunedì 20 marzo 2017, 22:05
da crap0101
uhm... nel codice che hai postato non mi pare che il messaggio sia un oggetto di tipo bytes, anche perchè ti darebbe errore quando utilizzi il metodo encode() che gli oggetti bytes non hanno.

Re: [Python3] encoding/decoding di caratteri

Inviato: lunedì 20 marzo 2017, 22:28
da rai
Sì `message' è sicuramente str (essendo somma di stringhe matchate con re).
Ma quando la prende sendmail dovrebbe essere diventata bytes dato che la passo come `message.encode() '
O almeno, salvo errori

Codice: Seleziona tutto

>>> type(stringa)
<class 'str'>
>>> type(stringa.encode())
<class 'bytes'>

Re: [Python3] encoding/decoding di caratteri

Inviato: martedì 21 marzo 2017, 0:35
da crap0101
sì giusto, quindi dev'essere proprio come dice la doc e - che sia stringa o che siano bytes - la codifica dev'essere ascii.
Credo quindi non ci sia molto da fare mandano la mail in quella modalità senza usare i mime con cui si può specificare la codifica che verrà poi letta dal prog destinatario.

Re: [Python3] encoding/decoding di caratteri

Inviato: martedì 21 marzo 2017, 16:55
da rai
Sì quindi faccio una cosa del genere:

Codice: Seleziona tutto

import smtplib
from email.mime.nonmultipart import MIMENonMultipart

SUBJECT = "prova"
FROM = user
PASSWORD = password
TO = ['destinatario1@mail.com', 'destinatario2@mail.it']
TEXT = " à € ì ò ù "
msg = MIMENonMultipart('text', 'plain', charset='utf-8')
msg['Subject'] = SUBJECT
msg['From'] = FROM
msg['To'] = ", ".join(TO)
msg.set_payload(TEXT + ' Creata col modulo email.mime.')

SERVER = "il.server.smtps"
smtp = smtplib.SMTP_SSL(SERVER)
smtp.login(FROM, PASSWORD)
smtp.sendmail(FROM, TO, msg.as_string().encode('utf-8'))
smtp.quit()
il che funziona comunque, anche se la mail è 100% ascii

Grazie, come sempre