[REF] mail,test_mail: prefer EmailMessage over deprecated Message
[PEP 594][1] is going to depreciate legacy `email.message.Message` API in PY3.8 in favor `email.message.EmailMessage`. All `email.message_from_x` constructor functions take an optional `policy` argument. The policy can either be `policy.compat32` to generate an `email.message.Message` instance, either be `policy.SMTP` to generate an `email.message.EmailMessage` instance. The [current][2] default policy is `policy.compat32` but will change in a future version of python. It is recommended to always specify the policy. [1]: https://www.python.org/dev/peps/pep-0594/#email-legacy-api [2] https://docs.python.org/3.7/library/email.parser.html#email.parser.BytesParser
This commit is contained in:
@@ -6,6 +6,7 @@ import io
|
||||
import re
|
||||
import logging
|
||||
import email
|
||||
import email.policy
|
||||
import dateutil
|
||||
import pytz
|
||||
import base64
|
||||
@@ -74,7 +75,7 @@ class FetchmailServer(models.Model):
|
||||
message = bytes(message.data)
|
||||
if isinstance(message, str):
|
||||
message = message.encode('utf-8')
|
||||
msg_txt = email.message_from_bytes(message)
|
||||
msg_txt = email.message_from_bytes(message, policy=email.policy.SMTP)
|
||||
|
||||
try:
|
||||
self._attachment_invoice(msg_txt)
|
||||
|
||||
@@ -5,6 +5,7 @@ import base64
|
||||
import datetime
|
||||
import dateutil
|
||||
import email
|
||||
import email.policy
|
||||
import hashlib
|
||||
import hmac
|
||||
import lxml
|
||||
@@ -20,7 +21,7 @@ except ImportError:
|
||||
import xmlrpclib
|
||||
|
||||
from collections import namedtuple
|
||||
from email.message import Message
|
||||
from email.message import EmailMessage
|
||||
from email.utils import formataddr
|
||||
from lxml import etree
|
||||
from werkzeug import url_encode
|
||||
@@ -876,8 +877,8 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
:raises: ValueError, TypeError
|
||||
"""
|
||||
if not isinstance(message, Message):
|
||||
raise TypeError('message must be an email.message.Message at this point')
|
||||
if not isinstance(message, EmailMessage):
|
||||
raise TypeError('message must be an email.message.EmailMessage at this point')
|
||||
catchall_alias = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.alias")
|
||||
bounce_alias = self.env['ir.config_parameter'].sudo().get_param("mail.bounce.alias")
|
||||
fallback_model = model
|
||||
@@ -1093,7 +1094,7 @@ class MailThread(models.AbstractModel):
|
||||
message = bytes(message.data)
|
||||
if isinstance(message, str):
|
||||
message = message.encode('utf-8')
|
||||
message = email.message_from_bytes(message)
|
||||
message = email.message_from_bytes(message, policy=email.policy.SMTP)
|
||||
|
||||
# parse the message, verify we are not in a loop by checking message_id is not duplicated
|
||||
msg_dict = self.message_parse(message, save_original=save_original)
|
||||
@@ -1301,8 +1302,8 @@ class MailThread(models.AbstractModel):
|
||||
:param message_dict: dictionary holding already-parsed values and in
|
||||
which bounce-related values will be added;
|
||||
"""
|
||||
if not isinstance(email_message, Message):
|
||||
raise TypeError('message must be an email.message.Message at this point')
|
||||
if not isinstance(email_message, EmailMessage):
|
||||
raise TypeError('message must be an email.message.EmailMessage at this point')
|
||||
|
||||
email_part = next((part for part in email_message.walk() if part.get_content_type() == 'message/rfc822'), None)
|
||||
dsn_part = next((part for part in email_message.walk() if part.get_content_type() == 'message/delivery-status'), None)
|
||||
@@ -1361,8 +1362,8 @@ class MailThread(models.AbstractModel):
|
||||
('file2', 'bytes')}
|
||||
}
|
||||
"""
|
||||
if not isinstance(message, Message):
|
||||
raise ValueError(_('Message should be a valid Message instance'))
|
||||
if not isinstance(message, EmailMessage):
|
||||
raise ValueError(_('Message should be a valid EmailMessage instance'))
|
||||
msg_dict = {'message_type': 'email'}
|
||||
|
||||
message_id = message.get('Message-Id')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import email
|
||||
import email.policy
|
||||
import socket
|
||||
|
||||
from email.utils import formataddr
|
||||
@@ -17,6 +18,9 @@ from odoo.addons.test_mail.tests.common import mail_new_test_user
|
||||
from odoo.tests import tagged
|
||||
from odoo.tools import email_split_and_format, mute_logger, pycompat
|
||||
|
||||
def from_string(text):
|
||||
return email.message_from_string(pycompat.to_text(text), policy=email.policy.SMTP)
|
||||
|
||||
|
||||
@tagged('mail_gateway')
|
||||
class TestEmailParsing(BaseFunctionalTest, MockEmails):
|
||||
@@ -24,16 +28,16 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
|
||||
def test_message_parse_body(self):
|
||||
# test pure plaintext
|
||||
plaintext = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>')
|
||||
res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(plaintext)))
|
||||
res = self.env['mail.thread'].message_parse(from_string(plaintext))
|
||||
self.assertIn('Please call me as soon as possible this afternoon!', res['body'])
|
||||
|
||||
# test multipart / text and html -> html has priority
|
||||
multipart = self.format(MAIL_TEMPLATE, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>')
|
||||
res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(multipart)))
|
||||
res = self.env['mail.thread'].message_parse(from_string(multipart))
|
||||
self.assertIn('<p>Please call me as soon as possible this afternoon!</p>', res['body'])
|
||||
|
||||
# test multipart / mixed
|
||||
res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_MULTIPART_MIXED)))
|
||||
res = self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_MULTIPART_MIXED))
|
||||
self.assertNotIn(
|
||||
'Should create a multipart/mixed: from gmail, *bold*, with attachment', res['body'],
|
||||
'message_parse: text version should not be in body after parsing multipart/mixed')
|
||||
@@ -41,7 +45,7 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
|
||||
'<div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>', res['body'],
|
||||
'message_parse: html version should be in body after parsing multipart/mixed')
|
||||
|
||||
res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_MULTIPART_MIXED_TWO)))
|
||||
res = self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_MULTIPART_MIXED_TWO))
|
||||
self.assertNotIn('First and second part', res['body'],
|
||||
'message_parse: text version should not be in body after parsing multipart/mixed')
|
||||
self.assertIn('First part', res['body'],
|
||||
@@ -49,24 +53,24 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
|
||||
self.assertIn('Second part', res['body'],
|
||||
'message_parse: second part of the html version should be in body after parsing multipart/mixed')
|
||||
|
||||
res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_SINGLE_BINARY)))
|
||||
res = self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_SINGLE_BINARY))
|
||||
self.assertEqual(res['body'], '')
|
||||
self.assertEqual(res['attachments'][0][0], 'thetruth.pdf')
|
||||
|
||||
def test_message_parse_eml(self):
|
||||
# Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed.
|
||||
mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
|
||||
self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(mail)))
|
||||
self.env['mail.thread'].message_parse(from_string(mail))
|
||||
|
||||
def test_message_parse_plaintext(self):
|
||||
""" Incoming email in plaintext should be stored as html """
|
||||
mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
|
||||
res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(mail)))
|
||||
res = self.env['mail.thread'].message_parse(from_string(mail))
|
||||
self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>', res['body'])
|
||||
|
||||
def test_message_parse_xhtml(self):
|
||||
# Test that the parsing of XHTML mails does not fail
|
||||
self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_XHTML)))
|
||||
self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_XHTML))
|
||||
|
||||
|
||||
@tagged('mail_gateway')
|
||||
|
||||
Reference in New Issue
Block a user