diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3636baa..4e3d256 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.3.0 +current_version = 3.0.1 commit = True tag = True tag_name = {new_version} @@ -7,4 +7,3 @@ tag_name = {new_version} [bumpversion:file:setup.py] search = VERSION = '{current_version}' replace = VERSION = '{new_version}' - diff --git a/.travis.yml b/.travis.yml index 23981f1..fed0b4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,18 @@ language: python python: - - "3.5" - -env: - - TOX_ENV=py27-flake8 - - TOX_ENV=py35-django110 - - TOX_ENV=py34-django110 - - TOX_ENV=py27-django110 - - TOX_ENV=py35-django19 - - TOX_ENV=py34-django19 - - TOX_ENV=py27-django19 - - TOX_ENV=py35-django18 - - TOX_ENV=py34-django18 - - TOX_ENV=py27-django18 + - "3.6" + - "3.7" + - "3.8" + - "3.9" install: - - pip install tox + - pip install tox-travis - pip install -r requirements.txt - pip install coveralls script: - - tox -e $TOX_ENV + - tox after_script: coveralls diff --git a/CHANGELOG b/CHANGELOG index a7c7393..6e1ee81 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,20 @@ +v3.0.1 +----- +Remove all newlines from subject + +v3.0 +----- +Add escaping to HTML parts in templates +Add tests for django main version + +v2.4 +----- +Add Python 3.7, 3.8 and 3.9 support +Drop Python <3.4 support +Add Django 3.1 and 3.2 support +Drop Django 3.0 support +Remove six dependency + v2.3 ----- Remove Django as explicit dependency diff --git a/README.rst b/README.rst index 170cc4c..e283891 100644 --- a/README.rst +++ b/README.rst @@ -24,6 +24,16 @@ develop branch: https://github.com/vintasoftware/django-templated-email/blob/dev stable pypi/master: https://github.com/vintasoftware/django-templated-email/blob/master/README.rst + +Requirements +================= +* Python (3.6, 3.7, 3.8, 3.9) +* Django (2.2, 3.1, 3.2) + +We **highly recommend** and only officially support the latest patch release of +each Python and Django series. + + Getting going - installation ============================== @@ -185,8 +195,8 @@ First get the image content from a file or a *ImageField*: .. code-block:: python # From a file - with open('lena.png', 'rb') as lena: - image = lena.read() + with open('pikachu.png', 'rb') as pikachu: + image = pikachu.read() # From an ImageField # Suppose we have this model @@ -201,7 +211,7 @@ Then create an instance of *InlineImage*: from templated_email import InlineImage - inline_image = InlineImage(filename="lena.png", content=image) + inline_image = InlineImage(filename="pikachu.png", content=image) Now pass the object on the context to the template when you send the email. @@ -210,13 +220,13 @@ Now pass the object on the context to the template when you send the email. send_templated_mail(template_name='welcome', from_email='from@example.com', recipient_list=['to@example.com'], - context={'lena_image': inline_image}) + context={'pikachu_image': inline_image}) Finally in your template add the image on the html template block: .. code-block:: html - + Note: All *InlineImage* objects you add to the context will be attached to the e-mail, even if they are not used in the template. @@ -315,6 +325,7 @@ Methods: **templated_email_get_recipients(self, form)** (mandatory): Return the recipient list to whom the email will be sent to. ie: + .. code-block:: python def templated_email_get_recipients(self, form): @@ -324,6 +335,7 @@ Methods: Use this method to add extra data to the context used for rendering the template. You should get the parent class's context from calling super. ie: + .. code-block:: python def templated_email_get_context_data(self, **kwargs): @@ -334,6 +346,7 @@ Methods: **templated_email_get_send_email_kwargs(self, valid, form)** (optional): Add or change the kwargs that will be used to send the e-mail. You should call super to get the default kwargs. ie: + .. code-block:: python def templated_email_get_send_email_kwargs(valid, form): @@ -356,7 +369,7 @@ Using django_templated_email in 3rd party applications If you would like to use django_templated_email to handle mail in a reusable application, you should note that: -* Your calls to **send_templated_mail** should set a value for **template_dir**, so you can keep copies of your app-specific templates local to your app (although the loader will find your email templates if you store them in */templates/templated_email*, if **TEMPLATED_EMAIL_TEMPLATE_DIR** has not been overidden) +* Your calls to **send_templated_mail** should set a value for **template_dir**, so you can keep copies of your app-specific templates local to your app (although the loader will find your email templates if you store them in */templates/templated_email*, if **TEMPLATED_EMAIL_TEMPLATE_DIR** has not been overridden) * If you do (and you should) set a value for **template_dir**, remember to include a trailing slash, i.e. *'my_app_email/'* * The deployed app may use a different backend which doesn't use the django templating backend, and as such make a note in your README warning developers that if they are using django_templated_email already, with a different backend, they will need to ensure their email provider can send all your templates (ideally enumerate those somewhere convenient) @@ -402,21 +415,25 @@ Execute the following commands:: bumpversion [major,minor,patch] python setup.py publish - git push origin master --tags + git push origin --tags Commercial Support ================== -This library, as others, is used in projects of Vinta clients. We are always looking for exciting work, so if you need any commercial support, feel free to get in touch: contact@vinta.com.br +.. image:: https://avatars2.githubusercontent.com/u/5529080?s=80&v=4 + :alt: Vinta Logo + :target: https://www.vinta.com.br + +This project, as other `Vinta Software `_ open-source projects is used in products of Vinta's clients. We are always looking for exciting work, so if you need any commercial support, feel free to get in touch: contact@vinta.com.br .. _Django: http://djangoproject.com .. |GitterBadge| image:: https://badges.gitter.im/vintasoftware/django-templated-email.svg .. _GitterBadge: https://gitter.im/vintasoftware/django-templated-email?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge -.. |TravisBadge| image:: https://travis-ci.org/vintasoftware/django-templated-email.svg?branch=develop -.. _TravisBadge: https://travis-ci.org/vintasoftware/django-templated-email +.. |TravisBadge| image:: https://travis-ci.com/vintasoftware/django-templated-email.svg?branch=develop +.. _TravisBadge: https://travis-ci.com/vintasoftware/django-templated-email .. |CoverageBadge| image:: https://coveralls.io/repos/github/vintasoftware/django-templated-email/badge.svg?branch=develop .. _CoverageBadge: https://coveralls.io/github/vintasoftware/django-templated-email?branch=develop .. |PypiversionBadge| image:: https://img.shields.io/pypi/v/django-templated-email.svg diff --git a/setup.py b/setup.py index 3f27738..5c5eb92 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages DESCRIPTION = "A Django oriented templated / transaction email abstraction" -VERSION = '2.3.0' +VERSION = '3.0.1' LONG_DESCRIPTION = None try: LONG_DESCRIPTION = open('README.rst').read() @@ -11,8 +11,7 @@ pass requirements = [ - 'django-render-block>=0.5', - 'six>=1', + 'django-render-block>=0.5' ] # python setup.py publish @@ -25,12 +24,10 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Topic :: Software Development :: Libraries :: Python Modules', 'Framework :: Django', ] @@ -46,6 +43,7 @@ license='MIT', description=DESCRIPTION, long_description=LONG_DESCRIPTION, + long_description_content_type='text/x-rst', platforms=['any'], classifiers=CLASSIFIERS, install_requires=requirements, diff --git a/templated_email/__init__.py b/templated_email/__init__.py index b004283..5803207 100644 --- a/templated_email/__init__.py +++ b/templated_email/__init__.py @@ -1,8 +1,6 @@ from django.conf import settings from django.utils.module_loading import import_string -import six - from templated_email.backends.vanilla_django import TemplateBackend from templated_email.utils import InlineImage # noqa @@ -20,7 +18,7 @@ def get_connection(backend=None, template_prefix=None, template_suffix=None, # django.core.mail.get_connection klass_path = backend or getattr(settings, 'TEMPLATED_EMAIL_BACKEND', TemplateBackend) - if isinstance(klass_path, six.string_types): + if isinstance(klass_path, str): try: # First check if class name is omitted and we have module in settings klass = import_string(klass_path + '.' + 'TemplateBackend') diff --git a/templated_email/backends/vanilla_django.py b/templated_email/backends/vanilla_django.py index 6eccaa5..58cef03 100644 --- a/templated_email/backends/vanilla_django.py +++ b/templated_email/backends/vanilla_django.py @@ -5,7 +5,7 @@ from django.conf import settings from django.core.mail import get_connection from django.template import Context -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from django.core.files.storage import default_storage from templated_email.utils import ( @@ -82,7 +82,6 @@ def _render_email(self, template_name, context, template_dir=None, file_extension=None): response = {} errors = {} - render_context = Context(context, autoescape=False) file_extension = file_extension or self.template_suffix if file_extension.startswith('.'): @@ -102,6 +101,7 @@ def _render_email(self, template_name, context, full_template_names.append(one_full_template_name) for part in ['subject', 'html', 'plain']: + render_context = Context(context, autoescape=(part == 'html')) try: response[part] = render_block_to_string(full_template_names, part, render_context) except BlockNotFound as error: @@ -158,7 +158,7 @@ def get_email_message(self, template_name, context, from_email=None, to=None, subject_template = subject_dict.get(template_name, _('%s email subject' % template_name)) subject = subject_template % context - subject = subject.strip('\n\r') # strip newlines from subject + subject = subject.strip('\n\r').replace('\n', ' ').replace('\r', ' ') # strip newlines from subject if not plain_part: plain_part = self._generate_plain_part(parts) diff --git a/templated_email/migrations/0001_initial.py b/templated_email/migrations/0001_initial.py index 2c5dc9d..dbed1fb 100644 --- a/templated_email/migrations/0001_initial.py +++ b/templated_email/migrations/0001_initial.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.10 on 2016-10-05 17:09 -from __future__ import unicode_literals from django.db import migrations, models import uuid diff --git a/templated_email/urls.py b/templated_email/urls.py index fd7eb53..c9708cf 100644 --- a/templated_email/urls.py +++ b/templated_email/urls.py @@ -1,8 +1,8 @@ -from django.conf.urls import url +from django.urls import re_path from templated_email.views import ShowEmailView app_name = 'templated_email' urlpatterns = [ - url(r'^email/(?P([a-f\d]{32})|([a-f\d]{8}(-[a-f\d]{4}){3}-[a-f\d]{12}))/$', ShowEmailView.as_view(), name='show_email'), + re_path(r'^email/(?P([a-f\d]{32})|([a-f\d]{8}(-[a-f\d]{4}){3}-[a-f\d]{12}))/$', ShowEmailView.as_view(), name='show_email'), ] diff --git a/templated_email/utils.py b/templated_email/utils.py index 2a9cb9f..3f3400a 100644 --- a/templated_email/utils.py +++ b/templated_email/utils.py @@ -6,12 +6,10 @@ from django.utils.module_loading import import_string from django.conf import settings -import six - def _get_klass_from_config(config_variable, default): klass_path = getattr(settings, config_variable, default) - if isinstance(klass_path, six.string_types): + if isinstance(klass_path, str): klass_path = import_string(klass_path) return klass_path diff --git a/tests/backends/test_vanilla_django_backend.py b/tests/backends/test_vanilla_django_backend.py index 5c702e3..db4bc7e 100644 --- a/tests/backends/test_vanilla_django_backend.py +++ b/tests/backends/test_vanilla_django_backend.py @@ -45,6 +45,18 @@ MULTI_TEMPLATE_SUBJECT_RESULT = 'A subject' +NON_ESCAPED_PLAIN_RESULT = (u'\n Hi,\n\n You just signed up for my website, using:\n ' + u' username:

vintasoftware

\n join date: Aug. 22, 2016\n' + u'\n Thanks, you rock!\n') + + +ESCAPED_HTML_RESULT = (u'

Hi Foo Bar,

You just signed up for my website, ' + u'using:

username
<p>vintasoftwar' + u'e</p>
join date
Aug. 22, 2016
' + u'

Thanks, you rock!

') + +NON_ESCAPED_SUBJECT_RESULT = 'My subject for

vintasoftware

' + TXT_FILE = 'test' @@ -72,43 +84,52 @@ def test_inexistent_base_email(self): try: self.backend._render_email('inexistent_base.email', {}) except TemplateDoesNotExist as e: - self.assertEquals(e.args[0], 'foo') + self.assertEqual(e.args[0], 'foo') def test_inexistent_template_email(self): try: self.backend._render_email('foo', {}) except TemplateDoesNotExist as e: - self.assertEquals(e.args[0], 'templated_email/foo.email') + self.assertEqual(e.args[0], 'templated_email/foo.email') def test_render_plain_email(self): response = self.backend._render_email( 'plain_template.email', self.context) - self.assertEquals(len(response.keys()), 2) - self.assertEquals(PLAIN_RESULT, response['plain']) - self.assertEquals(SUBJECT_RESULT, response['subject']) + self.assertEqual(len(response.keys()), 2) + self.assertEqual(PLAIN_RESULT, response['plain']) + self.assertEqual(SUBJECT_RESULT, response['subject']) def test_render_html_email(self): response = self.backend._render_email( 'html_template.email', self.context) - self.assertEquals(len(response.keys()), 2) + self.assertEqual(len(response.keys()), 2) self.assertHTMLEqual(HTML_RESULT, response['html']) - self.assertEquals(SUBJECT_RESULT, response['subject']) + self.assertEqual(SUBJECT_RESULT, response['subject']) def test_render_mixed_email(self): response = self.backend._render_email( 'mixed_template.email', self.context) - self.assertEquals(len(response.keys()), 3) + self.assertEqual(len(response.keys()), 3) self.assertHTMLEqual(HTML_RESULT, response['html']) - self.assertEquals(PLAIN_RESULT, response['plain']) - self.assertEquals(SUBJECT_RESULT, response['subject']) + self.assertEqual(PLAIN_RESULT, response['plain']) + self.assertEqual(SUBJECT_RESULT, response['subject']) def test_render_inheritance_email(self): response = self.backend._render_email( 'inheritance_template.email', self.context) - self.assertEquals(len(response.keys()), 3) + self.assertEqual(len(response.keys()), 3) self.assertHTMLEqual(INHERITANCE_RESULT, response['html']) - self.assertEquals(PLAIN_RESULT, response['plain']) - self.assertEquals('Another subject for vintasoftware', response['subject']) + self.assertEqual(PLAIN_RESULT, response['plain']) + self.assertEqual('Another subject for vintasoftware', response['subject']) + + def test_email_text_escaping(self): + self.context['username'] = '

vintasoftware

' + + response = self.backend._render_email( + 'mixed_template.email', self.context) + self.assertHTMLEqual(ESCAPED_HTML_RESULT, response['html']) + self.assertEqual(NON_ESCAPED_PLAIN_RESULT, response['plain']) + self.assertEqual(NON_ESCAPED_SUBJECT_RESULT, response['subject']) @patch.object( template_backend_klass, '_render_email', @@ -120,12 +141,12 @@ def test_get_email_message(self, mock): from_email='from@example.com', cc=['cc@example.com'], bcc=['bcc@example.com'], to=['to@example.com']) self.assertTrue(isinstance(message, EmailMessage)) - self.assertEquals(message.body, PLAIN_RESULT) - self.assertEquals(message.subject, SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.body, PLAIN_RESULT) + self.assertEqual(message.subject, SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @patch.object( template_backend_klass, '_render_email', @@ -145,7 +166,7 @@ def test_get_email_message_with_create_link(self, mocked): self.assertEqual(len(second_call_context), 0) saved_email = SavedEmail.objects.get( uuid=uuid) - self.assertEquals(saved_email.content, HTML_RESULT) + self.assertEqual(saved_email.content, HTML_RESULT) @patch.object( template_backend_klass, '_render_email', @@ -188,12 +209,12 @@ def test_get_email_message_without_subject(self, mock): from_email='from@example.com', cc=['cc@example.com'], bcc=['bcc@example.com'], to=['to@example.com']) self.assertTrue(isinstance(message, EmailMessage)) - self.assertEquals(message.body, PLAIN_RESULT) - self.assertEquals(message.subject, 'foo') - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.body, PLAIN_RESULT) + self.assertEqual(message.subject, 'foo') + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @override_settings(TEMPLATED_EMAIL_DJANGO_SUBJECTS={'foo.email': 'foo\r\n'}) @@ -207,12 +228,12 @@ def test_get_email_message_without_subject_multiple_templates(self, mock): from_email='from@example.com', cc=['cc@example.com'], bcc=['bcc@example.com'], to=['to@example.com']) self.assertTrue(isinstance(message, EmailMessage)) - self.assertEquals(message.body, PLAIN_RESULT) - self.assertEquals(message.subject, 'foo') - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.body, PLAIN_RESULT) + self.assertEqual(message.subject, 'foo') + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @patch.object( template_backend_klass, '_render_email', @@ -225,13 +246,13 @@ def test_get_email_message_generated_plain_text(self, mock): bcc=['bcc@example.com'], to=['to@example.com']) self.assertTrue(isinstance(message, EmailMultiAlternatives)) self.assertHTMLEqual(message.alternatives[0][0], HTML_RESULT) - self.assertEquals(message.alternatives[0][1], 'text/html') - self.assertEquals(message.body, GENERATED_PLAIN_RESULT) - self.assertEquals(message.subject, SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.alternatives[0][1], 'text/html') + self.assertEqual(message.body, GENERATED_PLAIN_RESULT) + self.assertEqual(message.subject, SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @patch.object( template_backend_klass, '_render_email', @@ -240,43 +261,43 @@ def test_get_email_message_generated_plain_text(self, mock): @override_settings(TEMPLATED_EMAIL_PLAIN_FUNCTION=lambda x: 'hi') def test_get_email_message_custom_func_generated_plain_text(self, mock): message = self.backend.get_email_message('foo.email', {}) - self.assertEquals(message.body, 'hi') + self.assertEqual(message.body, 'hi') def test_get_multi_match_last_email_message_generated_plain_text(self): message = self.backend.get_email_message( ['multi-template.email', 'foo.email', ], {}, from_email='from@example.com', cc=['cc@example.com'], bcc=['bcc@example.com'], to=['to@example.com']) - self.assertEquals(message.body, MULTI_TEMPLATE_PLAIN_RESULT) - self.assertEquals(message.subject, MULTI_TEMPLATE_SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.body, MULTI_TEMPLATE_PLAIN_RESULT) + self.assertEqual(message.subject, MULTI_TEMPLATE_SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') def test_get_multi_first_match_email_message_generated_plain_text(self): message = self.backend.get_email_message( ['foo.email', 'multi-template.email', ], {}, from_email='from@example.com', cc=['cc@example.com'], bcc=['bcc@example.com'], to=['to@example.com']) - self.assertEquals(message.body, MULTI_TEMPLATE_PLAIN_RESULT) - self.assertEquals(message.subject, MULTI_TEMPLATE_SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.body, MULTI_TEMPLATE_PLAIN_RESULT) + self.assertEqual(message.subject, MULTI_TEMPLATE_SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') def test_get_multi_options_select_last_plain_only(self): message = self.backend.get_email_message( ['non-existing.email', 'also-non-existing.email', 'non-existing-without-suffix', 'foo.email', 'multi-template.email', ], {}, from_email='from@example.com', cc=['cc@example.com'], bcc=['bcc@example.com'], to=['to@example.com']) - self.assertEquals(message.body, MULTI_TEMPLATE_PLAIN_RESULT) - self.assertEquals(message.subject, MULTI_TEMPLATE_SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.body, MULTI_TEMPLATE_PLAIN_RESULT) + self.assertEqual(message.subject, MULTI_TEMPLATE_SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @patch.object( template_backend_klass, '_render_email', @@ -290,13 +311,13 @@ def test_get_email_message_with_plain_and_html(self, mock): bcc=['bcc@example.com'], to=['to@example.com']) self.assertTrue(isinstance(message, EmailMultiAlternatives)) self.assertHTMLEqual(message.alternatives[0][0], HTML_RESULT) - self.assertEquals(message.alternatives[0][1], 'text/html') - self.assertEquals(message.body, PLAIN_RESULT) - self.assertEquals(message.subject, SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.alternatives[0][1], 'text/html') + self.assertEqual(message.body, PLAIN_RESULT) + self.assertEqual(message.subject, SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @patch.object( template_backend_klass, '_render_email', @@ -335,12 +356,12 @@ def test_get_email_message_html_only(self, mock): bcc=['bcc@example.com'], to=['to@example.com']) self.assertTrue(isinstance(message, EmailMessage)) self.assertHTMLEqual(message.body, HTML_RESULT) - self.assertEquals(message.content_subtype, 'html') - self.assertEquals(message.subject, SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com']) - self.assertEquals(message.cc, ['cc@example.com']) - self.assertEquals(message.bcc, ['bcc@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.content_subtype, 'html') + self.assertEqual(message.subject, SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com']) + self.assertEqual(message.cc, ['cc@example.com']) + self.assertEqual(message.bcc, ['bcc@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @patch.object( template_backend_klass, '_render_email', @@ -351,17 +372,17 @@ def test_send(self, render_mock): ret = self.backend.send('mixed_template', 'from@example.com', ['to@example.com', 'to2@example.com'], {}, headers={'Message-Id': 'a_message_id'}) - self.assertEquals(ret, 'a_message_id') - self.assertEquals(len(mail.outbox), 1) + self.assertEqual(ret, 'a_message_id') + self.assertEqual(len(mail.outbox), 1) message = mail.outbox[0] - self.assertEquals(ret, message.extra_headers['Message-Id']) + self.assertEqual(ret, message.extra_headers['Message-Id']) self.assertTrue(isinstance(message, EmailMultiAlternatives)) self.assertHTMLEqual(message.alternatives[0][0], HTML_RESULT) - self.assertEquals(message.alternatives[0][1], 'text/html') - self.assertEquals(message.body, PLAIN_RESULT) - self.assertEquals(message.subject, SUBJECT_RESULT) - self.assertEquals(message.to, ['to@example.com', 'to2@example.com']) - self.assertEquals(message.from_email, 'from@example.com') + self.assertEqual(message.alternatives[0][1], 'text/html') + self.assertEqual(message.body, PLAIN_RESULT) + self.assertEqual(message.subject, SUBJECT_RESULT) + self.assertEqual(message.to, ['to@example.com', 'to2@example.com']) + self.assertEqual(message.from_email, 'from@example.com') @patch.object( template_backend_klass, 'get_email_message' @@ -425,8 +446,8 @@ def test_send_attachment_mime_base(self, render_mock): ['to@example.com', 'to2@example.com'], {}, attachments=[MIMEImage(TXT_FILE, 'text/plain')]) attachment = mail.outbox[0].attachments[0] - self.assertEquals(decode_b64_msg(attachment.get_payload()), - TXT_FILE) + self.assertEqual(decode_b64_msg(attachment.get_payload()), + TXT_FILE) @patch.object( template_backend_klass, '_render_email', @@ -438,8 +459,8 @@ def test_send_attachment_tripple(self, render_mock): ['to@example.com', 'to2@example.com'], {}, attachments=[('black_pixel.png', TXT_FILE, 'text/plain')]) attachment = mail.outbox[0].attachments[0] - self.assertEquals(('black_pixel.png', TXT_FILE, 'text/plain'), - attachment) + self.assertEqual(('black_pixel.png', TXT_FILE, 'text/plain'), + attachment) @patch.object( template_backend_klass, '_render_email', @@ -452,8 +473,8 @@ def test_get_email_message_attachment_mime_base(self, mock): bcc=['bcc@example.com'], to=['to@example.com'], attachments=[MIMEImage(TXT_FILE, 'text/plain')]) attachment = message.attachments[0] - self.assertEquals(decode_b64_msg(attachment.get_payload()), - TXT_FILE) + self.assertEqual(decode_b64_msg(attachment.get_payload()), + TXT_FILE) @patch.object( template_backend_klass, '_render_email', @@ -466,14 +487,14 @@ def test_get_email_message_attachment_tripple(self, mock): bcc=['bcc@example.com'], to=['to@example.com'], attachments=[('black_pixel.png', TXT_FILE, 'text/plain')]) attachment = message.attachments[0] - self.assertEquals(('black_pixel.png', TXT_FILE, 'text/plain'), - attachment) + self.assertEqual(('black_pixel.png', TXT_FILE, 'text/plain'), + attachment) def test_removal_of_legacy(self): try: self.backend._render_email('legacy', {}) except TemplateDoesNotExist as e: - self.assertEquals(e.args[0], 'templated_email/legacy.email') + self.assertEqual(e.args[0], 'templated_email/legacy.email') def test_host_inline_image_if_not_exist(self): inline_image = InlineImage('foo.jpg', b'bar') @@ -484,7 +505,7 @@ def test_host_inline_image_if_not_exist(self): self.assertEqual(filename, '/media/saved_url') self.storage_mock.save.assert_called_once() name, content = self.storage_mock.save.call_args[0] - self.assertEquals( + self.assertEqual( name, 'templated_email/37b51d194a7513e45b56f6524f2d51f2foo.jpg') self.assertTrue(isinstance(content, BytesIO)) diff --git a/tests/backends/utils.py b/tests/backends/utils.py index 313bc16..1f85ab2 100644 --- a/tests/backends/utils.py +++ b/tests/backends/utils.py @@ -6,17 +6,17 @@ class TempalteBackendBaseMixin(object): @override_settings(TEMPLATED_EMAIL_TEMPLATE_DIR='test_prefix') def test_uses_prefix_from_config(self): backend = self.template_backend_klass() - self.assertEquals(backend.template_prefix, 'test_prefix') + self.assertEqual(backend.template_prefix, 'test_prefix') @override_settings(TEMPLATED_EMAIL_FILE_EXTENSION='test_suffix') def test_uses_suffix_from_config(self): backend = self.template_backend_klass() - self.assertEquals(backend.template_suffix, 'test_suffix') + self.assertEqual(backend.template_suffix, 'test_suffix') def test_override_prefix_from_config(self): backend = self.template_backend_klass(template_prefix='test_prefix') - self.assertEquals(backend.template_prefix, 'test_prefix') + self.assertEqual(backend.template_prefix, 'test_prefix') def test_override_suffix_from_config(self): backend = self.template_backend_klass(template_suffix='test_suffix') - self.assertEquals(backend.template_suffix, 'test_suffix') + self.assertEqual(backend.template_suffix, 'test_suffix') diff --git a/tests/generic_views/test_views.py b/tests/generic_views/test_views.py index 56bd89b..168b8c9 100644 --- a/tests/generic_views/test_views.py +++ b/tests/generic_views/test_views.py @@ -24,11 +24,11 @@ def test_templated_email_get_template_names_raises_exception(self): def test_templated_email_get_template_names_with_template_name(self): self.mixin_object.templated_email_template_name = 'template_name' - self.assertEquals( + self.assertEqual( self.mixin_object.templated_email_get_template_names(valid=True), ['template_name'] ) - self.assertEquals( + self.assertEqual( self.mixin_object.templated_email_get_template_names(valid=False), ['template_name'] ) @@ -59,11 +59,11 @@ class FakeForm(object): form = FakeForm() kwargs = self.mixin_object.templated_email_get_send_email_kwargs( valid=True, form=form) - self.assertEquals(len(kwargs), 4) - self.assertEquals(kwargs['template_name'], ['template']) - self.assertEquals(kwargs['from_email'], None) - self.assertEquals(kwargs['recipient_list'], ['foo@example.com']) - self.assertEquals(kwargs['context'], {'form_data': 'foo'}) + self.assertEqual(len(kwargs), 4) + self.assertEqual(kwargs['template_name'], ['template']) + self.assertEqual(kwargs['from_email'], None) + self.assertEqual(kwargs['recipient_list'], ['foo@example.com']) + self.assertEqual(kwargs['context'], {'form_data': 'foo'}) @mock.patch.object(TemplatedEmailFormViewMixin, 'templated_email_get_template_names', @@ -80,11 +80,11 @@ class FakeForm(object): form = FakeForm() kwargs = self.mixin_object.templated_email_get_send_email_kwargs( valid=False, form=form) - self.assertEquals(len(kwargs), 4) - self.assertEquals(kwargs['template_name'], ['template']) - self.assertEquals(kwargs['from_email'], None) - self.assertEquals(kwargs['recipient_list'], ['foo@example.com']) - self.assertEquals(kwargs['context'], {'form_errors': 'errors foo'}) + self.assertEqual(len(kwargs), 4) + self.assertEqual(kwargs['template_name'], ['template']) + self.assertEqual(kwargs['from_email'], None) + self.assertEqual(kwargs['recipient_list'], ['foo@example.com']) + self.assertEqual(kwargs['context'], {'form_errors': 'errors foo'}) class TemplatedEmailFormViewMixinTestCase(MockedNetworkTestCaseMixin, TestCase): @@ -101,48 +101,48 @@ def setUp(self): def test_form_valid_with_send_on_success(self): response = AuthorCreateView.as_view()(self.good_request) - self.assertEquals(response.status_code, 302) - self.assertEquals(Author.objects.count(), 1) - self.assertEquals(len(mail.outbox), 1) - self.assertEquals(mail.outbox[0].alternatives[0][0].strip(), - 'Andre - author@vinta.com.br') + self.assertEqual(response.status_code, 302) + self.assertEqual(Author.objects.count(), 1) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].alternatives[0][0].strip(), + 'Andre - author@vinta.com.br') def test_form_valid_with_send_on_success_false(self): default_value = AuthorCreateView.templated_email_send_on_success AuthorCreateView.templated_email_send_on_success = False response = AuthorCreateView.as_view()(self.good_request) - self.assertEquals(response.status_code, 302) - self.assertEquals(Author.objects.count(), 1) - self.assertEquals(len(mail.outbox), 0) + self.assertEqual(response.status_code, 302) + self.assertEqual(Author.objects.count(), 1) + self.assertEqual(len(mail.outbox), 0) AuthorCreateView.templated_email_send_on_success = default_value def test_form_invalid_with_not_send_on_failure(self): response = AuthorCreateView.as_view()(self.bad_request) - self.assertEquals(response.status_code, 200) - self.assertEquals(Author.objects.count(), 0) - self.assertEquals(len(mail.outbox), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual(Author.objects.count(), 0) + self.assertEqual(len(mail.outbox), 0) def test_form_invalid_with_send_on_failure(self): default_value = AuthorCreateView.templated_email_send_on_failure AuthorCreateView.templated_email_send_on_failure = True response = AuthorCreateView.as_view()(self.bad_request) - self.assertEquals(response.status_code, 200) - self.assertEquals(Author.objects.count(), 0) - self.assertEquals(len(mail.outbox), 1) - self.assertEquals(mail.outbox[0].alternatives[0][0].strip(), - '* Enter a valid email address.') + self.assertEqual(response.status_code, 200) + self.assertEqual(Author.objects.count(), 0) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].alternatives[0][0].strip(), + '* Enter a valid email address.') AuthorCreateView.templated_email_send_on_failure = default_value @override_settings(TEMPLATED_EMAIL_FROM_EMAIL='from@vinta.com.br') def test_from_email(self): AuthorCreateView.as_view()(self.good_request) - self.assertEquals(len(mail.outbox), 1) - self.assertEquals(mail.outbox[0].from_email, 'from@vinta.com.br') + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, 'from@vinta.com.br') def test_from_email_with_templated_email_from_email(self): default_value = AuthorCreateView.templated_email_from_email AuthorCreateView.templated_email_from_email = 'from2@vinta.com.br' AuthorCreateView.as_view()(self.good_request) - self.assertEquals(len(mail.outbox), 1) - self.assertEquals(mail.outbox[0].from_email, 'from2@vinta.com.br') + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, 'from2@vinta.com.br') AuthorCreateView.templated_email_from_email = default_value diff --git a/tests/test_inline_image.py b/tests/test_inline_image.py index f6f0408..85635d7 100644 --- a/tests/test_inline_image.py +++ b/tests/test_inline_image.py @@ -30,4 +30,4 @@ def test_cid_in_message(self): def test_image_in_attachments(self): mimage = self.message.attachments[0] attachment_content = base64.b64encode(mimage.get_payload(decode=True)) - self.assertEquals(attachment_content.decode(), imageb64) + self.assertEqual(attachment_content.decode(), imageb64) diff --git a/tests/test_urls.py b/tests/test_urls.py index c91a2b2..4b20eda 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -1,5 +1,6 @@ -from django.conf.urls import url, include +from django.conf.urls import include +from django.urls import re_path urlpatterns = [ - url(r'^', include('templated_email.urls', namespace='templated_email')), + re_path(r'^', include('templated_email.urls', namespace='templated_email')), ] diff --git a/tests/test_utils.py b/tests/test_utils.py index 4dc015b..10d80ae 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -48,7 +48,7 @@ def test_attach_to_message(self): message = mock.Mock() self.inline_image.attach_to_message(message) mimeimage = message.attach.call_args[0][0] - self.assertEquals(mimeimage.get('Content-ID'), - self.inline_image._content_id) - self.assertEquals(mimeimage.get('Content-Disposition'), - 'inline; filename="foo.png"') + self.assertEqual(mimeimage.get('Content-ID'), + self.inline_image._content_id) + self.assertEqual(mimeimage.get('Content-Disposition'), + 'inline; filename="foo.png"') diff --git a/tests/test_views.py b/tests/test_views.py index 815ffa2..f5358a1 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,8 +1,8 @@ import uuid +from django.urls import reverse from django.test import TestCase from django.test import Client -from django.core.urlresolvers import reverse from templated_email.models import SavedEmail @@ -18,24 +18,24 @@ def setUp(self): def test_get(self): response = self.client.get(self.url) - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) def test_get_hex(self): response = self.client.get(self.url_hex) - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) def test_get_non_valid(self): response = self.client.get('/email/bar/') - self.assertEquals(response.status_code, 404) + self.assertEqual(response.status_code, 404) def test_url(self): - self.assertEquals( + self.assertEqual( reverse('templated_email:show_email', kwargs={'uuid': self.saved_email.uuid}), self.url) def test_url_hex(self): - self.assertEquals( + self.assertEqual( reverse('templated_email:show_email', kwargs={'uuid': self.saved_email.uuid.hex}), self.url_hex) diff --git a/tox-requirements.txt b/tox-requirements.txt index fdd9d31..871945f 100644 --- a/tox-requirements.txt +++ b/tox-requirements.txt @@ -1,10 +1,9 @@ -django-anymail==3.0 -django-pytest==0.2.0 -django-render-block==0.5 -html2text==2016.5.29 -mock==2.0.0 -pytest==4.1.0 -pytest-django==3.4.5 -pytest-pythonpath==0.7.3 -six==1.10.0 -tox==2.3.1 +django-anymail +django-pytest +django-render-block +html2text +mock +pytest +pytest-django +pytest-pythonpath +tox diff --git a/tox.ini b/tox.ini index f75f5dd..342c95b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,8 @@ [tox] envlist = - py27-{flake8}, - {py27,py34,py35,py36}-django18, - {py27,py34,py35,py36}-django{19}, - {py27,py34,py35,py36}-django{110} - {py27,py34,py35,py36}-django{111} + py36-{flake8}, + {py36,py37,py38,py39}-django{22,31,32}, + {py38,py39}-djangomain, [testenv] @@ -13,12 +11,12 @@ commands= deps= -rtox-requirements.txt pytest-cov - django18: Django==1.8.14 - django19: Django==1.9.9 - django110: Django==1.10 - django111: Django==1.11 + django22: Django==2.2 + django31: Django==3.1 + django32: Django==3.2 + djangomain: https://github.com/django/django/archive/main.tar.gz -[testenv:py27-flake8] +[testenv:py36-flake8] commands = flake8 templated_email tests --ignore=E501 deps = flake8