1
0
Fork 0

Modernize syntax, drop Python EOL, drop drop Django<1.11, upgrade TravisCI config to v1, add Python 3.8 & Django 3.x

This commit is contained in:
Adam Dobrawy 2019-10-15 05:31:13 +02:00
parent f9b0a27e5f
commit 2b2b7d6392
43 changed files with 228 additions and 349 deletions

View file

@ -1,5 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Models declaration for application ``django_mailbox``.
@ -8,7 +7,9 @@ import gzip
from email.encoders import encode_base64
from email.message import Message as EmailMessage
from email.utils import formatdate, parseaddr
from urllib.parse import parse_qs, unquote, urlparse
from quopri import encode as encode_quopri
from io import BytesIO
import base64
import email
import logging
@ -18,17 +19,13 @@ import sys
import uuid
from tempfile import NamedTemporaryFile
import six
from six.moves.urllib.parse import parse_qs, unquote, urlparse
import django
from django.conf import settings as django_settings
from django.core.files.base import ContentFile, File
from django.core.mail.message import make_msgid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.utils.timezone import now
from django.utils.encoding import python_2_unicode_compatible
from django_mailbox import utils
from django_mailbox.signals import message_received
@ -41,20 +38,19 @@ logger = logging.getLogger(__name__)
class ActiveMailboxManager(models.Manager):
def get_queryset(self):
return super(ActiveMailboxManager, self).get_queryset().filter(
return super().get_queryset().filter(
active=True,
)
@python_2_unicode_compatible
class Mailbox(models.Model):
name = models.CharField(
_(u'Name'),
_('Name'),
max_length=255,
)
uri = models.CharField(
_(u'URI'),
_('URI'),
max_length=255,
help_text=(_(
"Example: imap+ssl://myusername:mypassword@someserver <br />"
@ -72,7 +68,7 @@ class Mailbox(models.Model):
)
from_email = models.CharField(
_(u'From email'),
_('From email'),
max_length=255,
help_text=(_(
"Example: MailBot &lt;mailbot@yourdomain.com&gt;<br />"
@ -89,7 +85,7 @@ class Mailbox(models.Model):
)
active = models.BooleanField(
_(u'Active'),
_('Active'),
help_text=(_(
"Check this e-mail inbox for new e-mail messages during polling "
"cycles. This checkbox does not have an effect upon whether "
@ -102,7 +98,7 @@ class Mailbox(models.Model):
)
last_polling = models.DateTimeField(
_(u"Last polling"),
_("Last polling"),
help_text=(_("The time of last successful polling for messages."
"It is blank for new mailboxes and is not set for "
"mailboxes that only receive messages via a pipe.")),
@ -294,7 +290,7 @@ class Mailbox(models.Model):
attachment.document.save(
uuid.uuid4().hex + extension,
ContentFile(
six.BytesIO(
BytesIO(
msg.get_payload(decode=True)
).getvalue()
)
@ -392,7 +388,7 @@ class Mailbox(models.Model):
with gzip.GzipFile(fileobj=fp_tmp, mode="w") as fp:
fp.write(message.as_string().encode('utf-8'))
msg.eml.save(
"%s.eml.gz" % (uuid.uuid4(), ),
"{}.eml.gz".format(uuid.uuid4()),
File(fp_tmp),
save=False
)
@ -430,41 +426,40 @@ class Mailbox(models.Model):
class IncomingMessageManager(models.Manager):
def get_queryset(self):
return super(IncomingMessageManager, self).get_queryset().filter(
return super().get_queryset().filter(
outgoing=False,
)
class OutgoingMessageManager(models.Manager):
def get_queryset(self):
return super(OutgoingMessageManager, self).get_queryset().filter(
return super().get_queryset().filter(
outgoing=True,
)
class UnreadMessageManager(models.Manager):
def get_queryset(self):
return super(UnreadMessageManager, self).get_queryset().filter(
return super().get_queryset().filter(
read=None
)
@python_2_unicode_compatible
class Message(models.Model):
mailbox = models.ForeignKey(
Mailbox,
related_name='messages',
verbose_name=_(u'Mailbox'),
verbose_name=_('Mailbox'),
on_delete=models.CASCADE
)
subject = models.CharField(
_(u'Subject'),
_('Subject'),
max_length=255
)
message_id = models.CharField(
_(u'Message ID'),
_('Message ID'),
max_length=255
)
@ -473,7 +468,7 @@ class Message(models.Model):
related_name='replies',
blank=True,
null=True,
verbose_name=_(u'In reply to'),
verbose_name=_('In reply to'),
on_delete=models.CASCADE
)
@ -483,21 +478,21 @@ class Message(models.Model):
)
to_header = models.TextField(
_(u'To header'),
_('To header'),
)
outgoing = models.BooleanField(
_(u'Outgoing'),
_('Outgoing'),
default=False,
blank=True,
)
body = models.TextField(
_(u'Body'),
_('Body'),
)
encoded = models.BooleanField(
_(u'Encoded'),
_('Encoded'),
default=False,
help_text=_('True if the e-mail body is Base64 encoded'),
)
@ -508,17 +503,17 @@ class Message(models.Model):
)
read = models.DateTimeField(
_(u'Read'),
_('Read'),
default=None,
blank=True,
null=True,
)
eml = models.FileField(
_(u'Raw message contents'),
_('Raw message contents'),
null=True,
upload_to="messages",
help_text=_(u'Original full content of message')
help_text=_('Original full content of message')
)
objects = models.Manager()
unread_messages = UnreadMessageManager()
@ -634,9 +629,9 @@ class Message(models.Model):
if encoding and encoding.lower() == 'quoted-printable':
# Cannot use `email.encoders.encode_quopri due to
# bug 14360: http://bugs.python.org/issue14360
output = six.BytesIO()
output = BytesIO()
encode_quopri(
six.BytesIO(
BytesIO(
attachment.document.read()
),
output,
@ -688,10 +683,8 @@ class Message(models.Model):
no fields existed for storing arbitrary bytes.
"""
if six.PY3:
body = body.encode('utf-8')
self.encoded = True
self.body = base64.b64encode(body).decode('ascii')
self.body = base64.b64encode(body.encode('utf-8')).decode('ascii')
def get_email_object(self):
"""Returns an `email.message.Message` instance representing the
@ -722,10 +715,7 @@ class Message(models.Model):
self.eml.close()
else:
body = self.get_body()
if six.PY3:
flat = email.message_from_bytes(body)
else:
flat = email.message_from_string(body)
flat = email.message_from_bytes(body)
self._email_object = self._rehydrate(flat)
return self._email_object
@ -734,7 +724,7 @@ class Message(models.Model):
for attachment in self.attachments.all():
# This attachment is attached only to this message.
attachment.delete()
return super(Message, self).delete(*args, **kwargs)
return super().delete(*args, **kwargs)
def __str__(self):
return self.subject
@ -744,7 +734,6 @@ class Message(models.Model):
verbose_name_plural = _('E-mail messages')
@python_2_unicode_compatible
class MessageAttachment(models.Model):
message = models.ForeignKey(
Message,
@ -756,30 +745,25 @@ class MessageAttachment(models.Model):
)
headers = models.TextField(
_(u'Headers'),
_('Headers'),
null=True,
blank=True,
)
document = models.FileField(
_(u'Document'),
_('Document'),
upload_to=utils.get_attachment_save_path,
)
def delete(self, *args, **kwargs):
"""Deletes the attachment."""
self.document.delete()
return super(MessageAttachment, self).delete(*args, **kwargs)
return super().delete(*args, **kwargs)
def _get_rehydrated_headers(self):
headers = self.headers
if headers is None:
return EmailMessage()
if sys.version_info < (3, 0):
try:
headers = headers.encode('utf-8')
except UnicodeDecodeError:
headers = headers.decode('utf-8').encode('utf-8')
return email.message_from_string(headers)
def _set_dehydrated_headers(self, email_object):
@ -798,7 +782,7 @@ class MessageAttachment(models.Model):
def get_filename(self):
"""Returns the original filename of this attachment."""
file_name = self._get_rehydrated_headers().get_filename()
if isinstance(file_name, six.string_types):
if isinstance(file_name, str):
result = utils.convert_header_to_unicode(file_name)
if result is None:
return file_name