diff --git a/des/__init__.py b/des/__init__.py index 1f09173..edb5eb3 100644 --- a/des/__init__.py +++ b/des/__init__.py @@ -1,2 +1,2 @@ -__version__ = '2.4.1' -default_app_config = 'des.apps.DjangoDesConfig' +__version__ = "3.0.0" +default_app_config = "des.apps.DjangoDesConfig" diff --git a/des/admin.py b/des/admin.py index 207f3ba..3f7682f 100644 --- a/des/admin.py +++ b/des/admin.py @@ -1,17 +1,18 @@ ## -*- coding: utf-8 -*- -from des.models import DynamicEmailConfiguration from django.contrib import admin from solo.admin import SingletonModelAdmin + from des.forms import DynamicEmailConfigurationForm +from des.models import DynamicEmailConfiguration class DynamicEmailConfigurationAdmin(SingletonModelAdmin): form = DynamicEmailConfigurationForm - change_form_template = 'des/change_form.html' + change_form_template = "des/change_form.html" + class Media: - js = ('js/des.js'), - css = { - 'all': ('css/des.css',) - } + js = (("js/des.js"),) + css = {"all": ("css/des.css",)} + admin.site.register(DynamicEmailConfiguration, DynamicEmailConfigurationAdmin) diff --git a/des/apps.py b/des/apps.py index 33d24ee..6beda48 100644 --- a/des/apps.py +++ b/des/apps.py @@ -3,6 +3,6 @@ class DjangoDesConfig(AppConfig): - name = 'des' - verbose_name = 'Dynamic Email Settings' + name = "des" + verbose_name = "Dynamic Email Settings" verbose_name_plural = verbose_name diff --git a/des/backends.py b/des/backends.py index 3ee5875..f7fdd8b 100644 --- a/des/backends.py +++ b/des/backends.py @@ -3,26 +3,40 @@ class ConfiguredEmailBackend(EmailBackend): - def __init__(self, host=None, port=None, username=None, password=None, - use_tls=None, fail_silently=None, use_ssl=None, timeout=None, - ssl_keyfile=None, ssl_certfile=None, - **kwargs): + def __init__( + self, + host=None, + port=None, + username=None, + password=None, + use_tls=None, + fail_silently=None, + use_ssl=None, + timeout=None, + ssl_keyfile=None, + ssl_certfile=None, + **kwargs + ): from des.models import DynamicEmailConfiguration + configuration = DynamicEmailConfiguration.get_solo() super(ConfiguredEmailBackend, self).__init__( - host = configuration.host if host is None else host, - port = configuration.port if port is None else port, - username = configuration.username if username is None else username, - password = configuration.password if password is None else password, - use_tls = configuration.use_tls if use_tls is None else use_tls, - fail_silently = configuration.fail_silently if fail_silently is None else fail_silently, - use_ssl = configuration.use_ssl if use_ssl is None else use_ssl, - timeout = configuration.timeout if timeout is None else timeout, - ssl_keyfile = ssl_keyfile, # TODO: configuration.ssl_keyfile if ssl_keyfile is not None else ssl_keyfile, - ssl_certfile = ssl_certfile, # TODO: configuration.ssl_certfile if ssl_certfile is not None else ssl_certfile, - **kwargs) + host=configuration.host if host is None else host, + port=configuration.port if port is None else port, + username=configuration.username if username is None else username, + password=configuration.password if password is None else password, + use_tls=configuration.use_tls if use_tls is None else use_tls, + fail_silently=( + configuration.fail_silently if fail_silently is None else fail_silently + ), + use_ssl=configuration.use_ssl if use_ssl is None else use_ssl, + timeout=configuration.timeout if timeout is None else timeout, + ssl_keyfile=ssl_keyfile, # TODO: configuration.ssl_keyfile if ssl_keyfile is not None else ssl_keyfile, + ssl_certfile=ssl_certfile, # TODO: configuration.ssl_certfile if ssl_certfile is not None else ssl_certfile, + **kwargs + ) -__all__ = ['ConfiguredEmailBackend'] +__all__ = ["ConfiguredEmailBackend"] diff --git a/des/forms.py b/des/forms.py index dd0724e..4ad9aca 100644 --- a/des/forms.py +++ b/des/forms.py @@ -1,4 +1,5 @@ from django.forms import ModelForm, PasswordInput + from des.models import DynamicEmailConfiguration diff --git a/des/helpers.py b/des/helpers.py index 8e013f6..229373c 100644 --- a/des/helpers.py +++ b/des/helpers.py @@ -1,4 +1,5 @@ from des.models import DynamicEmailConfiguration + try: from django.urls import reverse except ImportError: @@ -7,8 +8,7 @@ def get_configuration_admin_url(): meta = DynamicEmailConfiguration._meta - return reverse('admin:{}_{}_change'.format( - meta.app_label, meta.model_name - )) + return reverse("admin:{}_{}_change".format(meta.app_label, meta.model_name)) + -__all__ = ['get_configuration_admin_url'] +__all__ = ["get_configuration_admin_url"] diff --git a/des/migrations/0001_initial.py b/des/migrations/0001_initial.py index 27244fb..d9f3785 100644 --- a/des/migrations/0001_initial.py +++ b/des/migrations/0001_initial.py @@ -9,26 +9,77 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='DynamicEmailConfiguration', + name="DynamicEmailConfiguration", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('host', models.CharField(blank=True, max_length=256, null=True, verbose_name='Email Host')), - ('port', models.SmallIntegerField(blank=True, null=True, verbose_name='Email Port')), - ('from_email', models.CharField(blank=True, max_length=256, null=True, verbose_name='Default From Email')), - ('username', models.CharField(blank=True, max_length=256, null=True, verbose_name='Email Authentication Username')), - ('password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Email Authentication Password')), - ('use_tls', models.BooleanField(default=False, verbose_name='Use TLS')), - ('use_ssl', models.BooleanField(default=False, verbose_name='Use SSL')), - ('fail_silently', models.BooleanField(default=False, verbose_name='Fail Silently')), - ('timeout', models.SmallIntegerField(blank=True, null=True, verbose_name='Email Send Timeout (seconds)')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "host", + models.CharField( + blank=True, max_length=256, null=True, verbose_name="Email Host" + ), + ), + ( + "port", + models.SmallIntegerField( + blank=True, null=True, verbose_name="Email Port" + ), + ), + ( + "from_email", + models.CharField( + blank=True, + max_length=256, + null=True, + verbose_name="Default From Email", + ), + ), + ( + "username", + models.CharField( + blank=True, + max_length=256, + null=True, + verbose_name="Email Authentication Username", + ), + ), + ( + "password", + models.CharField( + blank=True, + max_length=256, + null=True, + verbose_name="Email Authentication Password", + ), + ), + ("use_tls", models.BooleanField(default=False, verbose_name="Use TLS")), + ("use_ssl", models.BooleanField(default=False, verbose_name="Use SSL")), + ( + "fail_silently", + models.BooleanField(default=False, verbose_name="Fail Silently"), + ), + ( + "timeout", + models.SmallIntegerField( + blank=True, + null=True, + verbose_name="Email Send Timeout (seconds)", + ), + ), ], options={ - 'verbose_name': 'Email Configuration', + "verbose_name": "Email Configuration", }, ), ] diff --git a/des/models.py b/des/models.py index bcc6fa9..47c945e 100644 --- a/des/models.py +++ b/des/models.py @@ -1,55 +1,60 @@ # -*- coding: utf-8 -*- +from django.core.exceptions import ValidationError from django.db import models -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as ugettext +from django.utils.translation import gettext_lazy as _ from solo.models import SingletonModel -from django.core.exceptions import ValidationError class DynamicEmailConfiguration(SingletonModel): host = models.CharField( - blank = True, null = True, - max_length = 256, verbose_name = _("Email Host")) + blank=True, null=True, max_length=256, verbose_name=_("Email Host") + ) - port = models.SmallIntegerField( - blank = True, null = True, - verbose_name = _("Email Port")) + port = models.SmallIntegerField(blank=True, null=True, verbose_name=_("Email Port")) from_email = models.CharField( - blank = True, null = True, - max_length = 256, verbose_name = _("Default From Email")) + blank=True, null=True, max_length=256, verbose_name=_("Default From Email") + ) username = models.CharField( - blank = True, null = True, - max_length = 256, verbose_name = _("Email Authentication Username")) + blank=True, + null=True, + max_length=256, + verbose_name=_("Email Authentication Username"), + ) password = models.CharField( - blank = True, null = True, - max_length = 256, verbose_name = _("Email Authentication Password")) + blank=True, + null=True, + max_length=256, + verbose_name=_("Email Authentication Password"), + ) - use_tls = models.BooleanField( - default = False, verbose_name = _("Use TLS")) + use_tls = models.BooleanField(default=False, verbose_name=_("Use TLS")) - use_ssl = models.BooleanField( - default = False, verbose_name = _("Use SSL")) + use_ssl = models.BooleanField(default=False, verbose_name=_("Use SSL")) - fail_silently = models.BooleanField( - default = False, verbose_name = _("Fail Silently")) + fail_silently = models.BooleanField(default=False, verbose_name=_("Fail Silently")) timeout = models.SmallIntegerField( - blank = True, null = True, - verbose_name = _("Email Send Timeout (seconds)")) + blank=True, null=True, verbose_name=_("Email Send Timeout (seconds)") + ) def clean(self): if self.use_ssl and self.use_tls: raise ValidationError( - _("\"Use TLS\" and \"Use SSL\" are mutually exclusive, " - "so only set one of those settings to True.")) + _( + '"Use TLS" and "Use SSL" are mutually exclusive, ' + "so only set one of those settings to True." + ) + ) def __str__(self): - return _("Email Configuration") + return ugettext("Email Configuration") class Meta: verbose_name = _("Email Configuration") -__all__ = ['DynamicEmailConfiguration'] +__all__ = ["DynamicEmailConfiguration"] diff --git a/des/urls.py b/des/urls.py index 71fae33..ec89138 100644 --- a/des/urls.py +++ b/des/urls.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -from django.conf.urls import url -from des import views +from django.urls import path +from des import views urlpatterns = [ - url(r'^send-test-email', views.send_test_email, name = 'des-test-email'), + path("send-test-email", views.send_test_email, name="des-test-email"), ] -__all__ = ['urlpatterns'] +__all__ = ["urlpatterns"] diff --git a/des/views.py b/des/views.py index a663e9a..cc0fb25 100644 --- a/des/views.py +++ b/des/views.py @@ -1,16 +1,17 @@ -from django.http import HttpResponseRedirect, HttpResponseNotFound -from django.views.decorators.http import require_http_methods -from django.contrib import messages -from django.template import loader from django.conf import settings +from django.contrib import messages from django.core.mail import send_mail -from django.utils.translation import ugettext as _ -from des.models import DynamicEmailConfiguration +from django.http import HttpResponseNotFound, HttpResponseRedirect +from django.template import loader +from django.utils.translation import gettext_lazy as _ +from django.views.decorators.http import require_http_methods + from des.helpers import get_configuration_admin_url +from des.models import DynamicEmailConfiguration -subject = getattr(settings, 'DES_TEST_SUBJECT', _("Test Email")) -text_template = getattr(settings, 'DES_TEST_TEXT_TEMPLATE', "des/test_email.txt") -html_template = getattr(settings, 'DES_TEST_HTML_TEMPLATE', None) +subject = getattr(settings, "DES_TEST_SUBJECT", _("Test Email")) +text_template = getattr(settings, "DES_TEST_TEXT_TEMPLATE", "des/test_email.txt") +html_template = getattr(settings, "DES_TEST_HTML_TEMPLATE", None) message_text = loader.render_to_string(text_template) message_html = loader.render_to_string(html_template) if html_template else None @@ -22,7 +23,7 @@ def send_test_email(request): if request.user is None or not request.user.is_staff: return HttpResponseNotFound() - email = request.POST.get('email', None) + email = request.POST.get("email", None) config = DynamicEmailConfiguration.get_solo() if email: @@ -32,14 +33,15 @@ def send_test_email(request): message_text, config.from_email or None, [email], - html_message = message_html) - - messages.success(request, - _("Test email sent. Please check \"{}\" for a " - "message with the subject \"{}\"").format( - email, - subject - ) + html_message=message_html, + ) + + messages.success( + request, + _( + 'Test email sent. Please check "{}" for a ' + 'message with the subject "{}"' + ).format(email, subject), ) except Exception as e: messages.error(request, _("Could not send email. {}").format(e)) @@ -49,4 +51,4 @@ def send_test_email(request): return HttpResponseRedirect(get_configuration_admin_url()) -__all__ = ['send_test_email'] +__all__ = ["send_test_email"] diff --git a/docs/conf.py b/docs/conf.py index f4e2e2a..198de36 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,12 +11,13 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import os +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) cwd = os.getcwd() parent = os.path.dirname(cwd) @@ -27,27 +28,27 @@ # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] +extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Django Dynamic Email Settings' -copyright = u'2017, Jamie Counsell' +project = "Django Dynamic Email Settings" +copyright = "2017, Jamie Counsell" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -60,161 +61,164 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'django-desdoc' +htmlhelp_basename = "django-desdoc" # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'django-des.tex', u'Django Dynamic Email Settings Documentation', - u'Jamie Counsell', 'manual'), + ( + "index", + "django-des.tex", + "Django Dynamic Email Settings Documentation", + "Jamie Counsell", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -222,12 +226,17 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'django-des', u'Django Dynamic Email Settings Documentation', - [u'Jamie Counsell'], 1) + ( + "index", + "django-des", + "Django Dynamic Email Settings Documentation", + ["Jamie Counsell"], + 1, + ) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ @@ -236,19 +245,25 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'django-des', u'Django Dynamic Email Settings Documentation', - u'Jamie Counsell', 'django-des', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "django-des", + "Django Dynamic Email Settings Documentation", + "Jamie Counsell", + "django-des", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/example/example/settings.py b/example/example/settings.py index 8917fa0..676f3a3 100644 --- a/example/example/settings.py +++ b/example/example/settings.py @@ -29,56 +29,56 @@ # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'des', - + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "des", # if your app has other dependencies that need to be added to the site # they should be added here ] MIDDLEWARE_CLASSES = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'example.urls' +ROOT_URLCONF = "example.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates'), ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), + ], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'example.wsgi.application' +WSGI_APPLICATION = "example.wsgi.application" # Database # https://docs.djangoproject.com/en/1.9/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -87,25 +87,25 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] # Internationalization # https://docs.djangoproject.com/en/1.9/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -116,11 +116,11 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.9/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" # Email configuration -EMAIL_BACKEND = 'des.backends.ConfiguredEmailBackend' +EMAIL_BACKEND = "des.backends.ConfiguredEmailBackend" DES_TEST_SUBJECT = "My Test Email Subject" DES_TEST_TEXT_TEMPLATE = "email/test_email.txt" diff --git a/example/example/urls.py b/example/example/urls.py index 310cd00..6abe733 100644 --- a/example/example/urls.py +++ b/example/example/urls.py @@ -13,12 +13,13 @@ 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ -from django.conf.urls import url, include + from django.contrib import admin -from des import urls as des_urls +from django.urls import include, path +from des import urls as des_urls urlpatterns = [ - url(r'^admin/', admin.site.urls), - url(r'^django-des/', include(des_urls)), + path("admin/", admin.site.urls), + path("django-des/", include(des_urls)), ] diff --git a/manage.py b/manage.py index 8c5be8a..404ea64 100755 --- a/manage.py +++ b/manage.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import unicode_literals, absolute_import +from __future__ import absolute_import, unicode_literals import os import sys diff --git a/requirements.txt b/requirements.txt index b480c81..95e7c98 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -django-solo==1.1.3 +django-solo==2.3.0 diff --git a/requirements_test.txt b/requirements_test.txt index ae74293..767801c 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,8 +1,5 @@ -coverage==4.4.1 -mock>=1.0.1 -flake8>=2.1.0 -tox>=1.7.0 -codecov>=2.0.0 - - -# Additional test requirements go here +coverage==7.4.1 +mock==5.1.0 +flake8==7.0.0 +tox==4.12.1 +codecov==2.1.13 diff --git a/runtests.py b/runtests.py index a68cf32..224890f 100644 --- a/runtests.py +++ b/runtests.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -from __future__ import unicode_literals, absolute_import +from __future__ import absolute_import, unicode_literals import os import sys @@ -12,9 +12,9 @@ def run_tests(*test_args): if not test_args: - test_args = ['tests'] + test_args = ["tests"] - os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings' + os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings" django.setup() TestRunner = get_runner(settings) test_runner = TestRunner() @@ -22,5 +22,5 @@ def run_tests(*test_args): sys.exit(bool(failures)) -if __name__ == '__main__': +if __name__ == "__main__": run_tests(*sys.argv[1:]) diff --git a/setup.cfg b/setup.cfg index c041ddc..a1530ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.4.1 +current_version = 3.0.0 commit = True tag = True diff --git a/setup.py b/setup.py index 09d17cb..64ab0f1 100755 --- a/setup.py +++ b/setup.py @@ -14,70 +14,64 @@ def get_version(*file_paths): """Retrieves the version from des/__init__.py""" filename = os.path.join(os.path.dirname(__file__), *file_paths) version_file = open(filename).read() - version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", - version_file, re.M) + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) - raise RuntimeError('Unable to find version string.') + raise RuntimeError("Unable to find version string.") version = get_version("des", "__init__.py") -if sys.argv[-1] == 'publish': +if sys.argv[-1] == "publish": try: import wheel + print("Wheel version: ", wheel.__version__) except ImportError: print('Wheel library missing. Please run "pip install wheel"') sys.exit() - os.system('python setup.py sdist upload') - os.system('python setup.py bdist_wheel upload') + os.system("python setup.py sdist upload") + os.system("python setup.py bdist_wheel upload") sys.exit() -if sys.argv[-1] == 'tag': +if sys.argv[-1] == "tag": print("Tagging the version on git:") os.system("git tag -a %s -m 'version %s'" % (version, version)) os.system("git push --tags") sys.exit() -readme = open('README.rst').read() -history = open('HISTORY.rst').read().replace('.. :changelog:', '') +readme = open("README.rst").read() +history = open("HISTORY.rst").read().replace(".. :changelog:", "") setup( - name='django-des', + name="django-des", version=version, description="""A reusable Django application and EmailBackend that allows email configuration to be changed while the server is running.""", - long_description=readme + '\n\n' + history, - author='Jamie Counsell', - author_email='jamiecounsell@me.com', - url='https://github.com/jamiecounsell/django-des', + long_description=readme + "\n\n" + history, + author="Jamie Counsell", + author_email="jamiecounsell@me.com", + url="https://github.com/jamiecounsell/django-des", packages=[ - 'des', + "des", ], include_package_data=True, - install_requires=[ - 'django-solo==1.1.3' - ], + install_requires=["django-solo==2.3.0"], license="MIT", zip_safe=False, - keywords='django-des', + keywords="django-des", classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Framework :: Django', - 'Framework :: Django :: 1.8', - 'Framework :: Django :: 1.9', - 'Framework :: Django :: 1.10', - 'Framework :: Django :: 1.11', - 'Framework :: Django :: 2.0', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - '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', + "Development Status :: 5 - Production/Stable", + "Framework :: Django", + "Framework :: Django :: 3.2", + "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ], ) diff --git a/tests/settings.py b/tests/settings.py index 8548450..04b5247 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -from __future__ import unicode_literals, absolute_import +from __future__ import absolute_import, unicode_literals import django @@ -24,6 +24,7 @@ "django.contrib.sites", "django.contrib.auth", "django.contrib.sessions", + "django.contrib.messages", "des", ] @@ -31,37 +32,39 @@ if django.VERSION >= (1, 10): MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] else: MIDDLEWARE_CLASSES = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.auth.middleware.SessionAuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] + +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" diff --git a/tests/test_admin.py b/tests/test_admin.py index fa1da96..51528a9 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -3,21 +3,21 @@ from django.contrib.auth.models import User from django.test import Client, TestCase + from des.helpers import get_configuration_admin_url class AdminTestCase(TestCase): def setUp(self): - self.password = 'secret' - self.admin= User.objects.create_superuser( - 'admin', 'example-email@website.com', self.password) + self.password = "secret" + self.admin = User.objects.create_superuser( + "admin", "example-email@website.com", self.password + ) self.client = Client() def test_custom_form_displays(self): url = get_configuration_admin_url() - self.client.login( - username = self.admin.username, - password = self.password) + self.client.login(username=self.admin.username, password=self.password) response = self.client.get(url) - self.assertIn('des--test-button', str(response.content)) + self.assertIn("des--test-button", str(response.content)) diff --git a/tests/test_backends.py b/tests/test_backends.py index 32cb28d..c524dea 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- import traceback + from django.test import TestCase, override_settings -from des.models import DynamicEmailConfiguration + from des.backends import ConfiguredEmailBackend +from des.models import DynamicEmailConfiguration + try: from django.urls import reverse except ImportError: @@ -16,20 +19,15 @@ def setUp(self): def test_backend_does_not_permit_mutex_tls_and_ssl(self): try: - ConfiguredEmailBackend( - use_tls = True, - use_ssl = True - ) + ConfiguredEmailBackend(use_tls=True, use_ssl=True) self.fail("No exception thrown. Expected ValueError") except ValueError: pass # Test succeeded except Exception: - self.fail("Incorrect exception thrown: {}".format( - traceback.format_exc() - )) + self.fail("Incorrect exception thrown: {}".format(traceback.format_exc())) def test_backend_honors_configured_host(self): - host = 'testhost.mysite.com' + host = "testhost.mysite.com" self.configuration.host = host self.configuration.save() backend = ConfiguredEmailBackend() @@ -43,14 +41,14 @@ def test_backend_honors_configured_port(self): self.assertEqual(backend.port, port) def test_backend_honors_configured_username(self): - username = 'awesomeuser' + username = "awesomeuser" self.configuration.username = username self.configuration.save() backend = ConfiguredEmailBackend() self.assertEqual(backend.username, username) def test_backend_honors_configured_password(self): - password = 'secret' + password = "secret" self.configuration.password = password self.configuration.save() backend = ConfiguredEmailBackend() @@ -113,27 +111,22 @@ def setUp(self): def test_backend_does_not_permit_mutex_tls_and_ssl(self): try: - ConfiguredEmailBackend( - use_tls = True, - use_ssl = True - ) + ConfiguredEmailBackend(use_tls=True, use_ssl=True) self.fail("No exception thrown. Expected ValueError") except ValueError: pass # Test succeeded except Exception: - self.fail("Incorrect exception thrown: {}".format( - traceback.format_exc() - )) + self.fail("Incorrect exception thrown: {}".format(traceback.format_exc())) - @override_settings(EMAIL_HOST = 'testhost.mysite.com') + @override_settings(EMAIL_HOST="testhost.mysite.com") def test_backend_honors_fallback_host(self): - host = 'testhost.mysite.com' + host = "testhost.mysite.com" self.configuration.host = None self.configuration.save() backend = ConfiguredEmailBackend() self.assertEqual(backend.host, host) - @override_settings(EMAIL_PORT = 123) + @override_settings(EMAIL_PORT=123) def test_backend_honors_fallback_port(self): port = 123 self.configuration.port = None @@ -141,23 +134,23 @@ def test_backend_honors_fallback_port(self): backend = ConfiguredEmailBackend() self.assertEqual(backend.port, port) - @override_settings(EMAIL_HOST_USER = 'awesomeuser') + @override_settings(EMAIL_HOST_USER="awesomeuser") def test_backend_honors_fallback_username(self): - username = 'awesomeuser' + username = "awesomeuser" self.configuration.username = None self.configuration.save() backend = ConfiguredEmailBackend() self.assertEqual(backend.username, username) - @override_settings(EMAIL_HOST_PASSWORD = 'secret') + @override_settings(EMAIL_HOST_PASSWORD="secret") def test_backend_honors_fallback_password(self): - password = 'secret' + password = "secret" self.configuration.password = None self.configuration.save() backend = ConfiguredEmailBackend() self.assertEqual(backend.password, password) - @override_settings(EMAIL_TIMEOUT = 12345) + @override_settings(EMAIL_TIMEOUT=12345) def test_backend_honors_fallback_timeout(self): timeout = 12345 self.configuration.timeout = None @@ -173,26 +166,19 @@ def setUp(self): def test_backend_does_not_permit_mutex_tls_and_ssl(self): try: - ConfiguredEmailBackend( - use_tls = True, - use_ssl = True - ) + ConfiguredEmailBackend(use_tls=True, use_ssl=True) self.fail("No exception thrown. Expected ValueError") except ValueError: pass # Test succeeded except Exception: - self.fail("Incorrect exception thrown: {}".format( - traceback.format_exc() - )) + self.fail("Incorrect exception thrown: {}".format(traceback.format_exc())) def test_backend_honors_explicit_host(self): - host = 'testhost.mysite.com' - explicit_host = 'anotherhost.mysite.com' + host = "testhost.mysite.com" + explicit_host = "anotherhost.mysite.com" self.configuration.host = host self.configuration.save() - backend = ConfiguredEmailBackend( - host = explicit_host - ) + backend = ConfiguredEmailBackend(host=explicit_host) self.assertEqual(backend.host, explicit_host) def test_backend_honors_explicit_port(self): @@ -200,29 +186,23 @@ def test_backend_honors_explicit_port(self): explicit_port = 321 self.configuration.port = port self.configuration.save() - backend = ConfiguredEmailBackend( - port = explicit_port - ) + backend = ConfiguredEmailBackend(port=explicit_port) self.assertEqual(backend.port, explicit_port) def test_backend_honors_explicit_username(self): - username = 'awesomeuser' - explicit_username = 'anotheruser' + username = "awesomeuser" + explicit_username = "anotheruser" self.configuration.username = username self.configuration.save() - backend = ConfiguredEmailBackend( - username = explicit_username - ) + backend = ConfiguredEmailBackend(username=explicit_username) self.assertEqual(backend.username, explicit_username) def test_backend_honors_explicit_password(self): - password = 'secret' - explicit_password = 'anothersecret' + password = "secret" + explicit_password = "anothersecret" self.configuration.password = password self.configuration.save() - backend = ConfiguredEmailBackend( - password = explicit_password - ) + backend = ConfiguredEmailBackend(password=explicit_password) self.assertEqual(backend.password, explicit_password) def test_backend_honors_explicit_use_tls_true(self): @@ -230,9 +210,7 @@ def test_backend_honors_explicit_use_tls_true(self): explicit_use_tls = False self.configuration.use_tls = use_tls self.configuration.save() - backend = ConfiguredEmailBackend( - use_tls = explicit_use_tls - ) + backend = ConfiguredEmailBackend(use_tls=explicit_use_tls) self.assertEqual(backend.use_tls, explicit_use_tls) def test_backend_honors_explicit_use_ssl_true(self): @@ -240,9 +218,7 @@ def test_backend_honors_explicit_use_ssl_true(self): explicit_use_ssl = False self.configuration.use_ssl = use_ssl self.configuration.save() - backend = ConfiguredEmailBackend( - use_ssl = explicit_use_ssl - ) + backend = ConfiguredEmailBackend(use_ssl=explicit_use_ssl) self.assertEqual(backend.use_ssl, explicit_use_ssl) def test_backend_honors_explicit_fail_silently_true(self): @@ -250,9 +226,7 @@ def test_backend_honors_explicit_fail_silently_true(self): explicit_fail_silently = False self.configuration.fail_silently = fail_silently self.configuration.save() - backend = ConfiguredEmailBackend( - fail_silently = explicit_fail_silently - ) + backend = ConfiguredEmailBackend(fail_silently=explicit_fail_silently) self.assertEqual(backend.fail_silently, explicit_fail_silently) def test_backend_honors_explicit_use_tls_false(self): @@ -260,9 +234,7 @@ def test_backend_honors_explicit_use_tls_false(self): explicit_use_tls = True self.configuration.use_tls = use_tls self.configuration.save() - backend = ConfiguredEmailBackend( - use_tls = explicit_use_tls - ) + backend = ConfiguredEmailBackend(use_tls=explicit_use_tls) self.assertEqual(backend.use_tls, explicit_use_tls) def test_backend_honors_explicit_use_ssl_false(self): @@ -270,9 +242,7 @@ def test_backend_honors_explicit_use_ssl_false(self): explicit_use_ssl = True self.configuration.use_ssl = use_ssl self.configuration.save() - backend = ConfiguredEmailBackend( - use_ssl = explicit_use_ssl - ) + backend = ConfiguredEmailBackend(use_ssl=explicit_use_ssl) self.assertEqual(backend.use_ssl, explicit_use_ssl) def test_backend_honors_explicit_fail_silently_false(self): @@ -280,9 +250,7 @@ def test_backend_honors_explicit_fail_silently_false(self): explicit_fail_silently = True self.configuration.fail_silently = fail_silently self.configuration.save() - backend = ConfiguredEmailBackend( - fail_silently = explicit_fail_silently - ) + backend = ConfiguredEmailBackend(fail_silently=explicit_fail_silently) self.assertEqual(backend.fail_silently, explicit_fail_silently) def test_backend_honors_explicit_timeout(self): @@ -290,7 +258,5 @@ def test_backend_honors_explicit_timeout(self): explicit_timeout = 54321 self.configuration.timeout = timeout self.configuration.save() - backend = ConfiguredEmailBackend( - timeout = explicit_timeout - ) + backend = ConfiguredEmailBackend(timeout=explicit_timeout) self.assertEqual(backend.timeout, explicit_timeout) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index f8d4387..877e527 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from django.test import TestCase + from des.helpers import get_configuration_admin_url + try: from django.urls import reverse except ImportError: @@ -11,5 +13,5 @@ class HelpersTestCase(TestCase): def test_get_configuration_admin_url_returns_correct_url(self): url = get_configuration_admin_url() - correct_url = reverse('admin:des_dynamicemailconfiguration_change') + correct_url = reverse("admin:des_dynamicemailconfiguration_change") self.assertEqual(url, correct_url) diff --git a/tests/test_models.py b/tests/test_models.py index 8b6b7aa..837f64c 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- import traceback -from django.test import TestCase + from django.core.exceptions import ValidationError +from django.test import TestCase + from des.models import DynamicEmailConfiguration @@ -18,11 +20,9 @@ def test_use_ssl_and_use_tls_raises(self): configuration.clean() self.fail("No exception thrown. Expected ValidationError") except ValidationError: - pass # Test succeeded + pass # Test succeeded except Exception: - self.fail("Incorrect exception thrown: {}".format( - traceback.format_exc() - )) + self.fail("Incorrect exception thrown: {}".format(traceback.format_exc())) def test_use_tls_and_not_use_ssl_works(self): try: diff --git a/tests/test_views.py b/tests/test_views.py index 7d300e8..27bafa4 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- -from django.test import TestCase, Client, override_settings from smtplib import SMTPException + from django.contrib.auth.models import User -from des.models import DynamicEmailConfiguration +from django.test import Client, TestCase, override_settings from mock.mock import patch + +from des.models import DynamicEmailConfiguration + try: from django.urls import reverse except ImportError: @@ -13,83 +16,70 @@ class ViewsTestCase(TestCase): def setUp(self): - self.password = 'secret' - self.admin= User.objects.create_superuser( - 'admin', 'example-email@website.com', self.password) - self.non_admin= User.objects.create_user( - 'notadmin', 'example-email@anotherwebsite.com', self.password) + self.password = "secret" + self.admin = User.objects.create_superuser( + "admin", "example-email@website.com", self.password + ) + self.non_admin = User.objects.create_user( + "notadmin", "example-email@anotherwebsite.com", self.password + ) self.client = Client() - self.client.login( - username = self.admin.username, - password = self.password) + self.client.login(username=self.admin.username, password=self.password) self.configuration = DynamicEmailConfiguration() - - @patch('des.views.send_mail', return_value = 1) + @patch("des.views.send_mail", return_value=1) def test_send_test_email_send_mail_invoked(self, send_mail): - url = reverse('des-test-email') - email = 'testemail@website.com' - response = self.client.post(url, { - 'email': email - }) + url = reverse("des-test-email") + email = "testemail@website.com" + response = self.client.post(url, {"email": email}) self.assertTrue(send_mail.call_count) - @patch('des.views.send_mail', return_value = 1) + @patch("des.views.send_mail", return_value=1) def test_send_test_email_send_mail_not_invoked_for_non_admin(self, send_mail): self.client.logout() - self.client.login( - username = self.non_admin.username, - password = self.password) - url = reverse('des-test-email') - email = 'testemail@website.com' - response = self.client.post(url, { - 'email': email - }) + self.client.login(username=self.non_admin.username, password=self.password) + url = reverse("des-test-email") + email = "testemail@website.com" + response = self.client.post(url, {"email": email}) - @patch('des.views.send_mail', return_value = 1) + @patch("des.views.send_mail", return_value=1) def test_send_test_email_hides_endpoint_for_non_admin(self, send_mail): self.client.logout() - self.client.login( - username = self.non_admin.username, - password = self.password) - url = reverse('des-test-email') - email = 'testemail@website.com' - response = self.client.post(url, { - 'email': email - }) + self.client.login(username=self.non_admin.username, password=self.password) + url = reverse("des-test-email") + email = "testemail@website.com" + response = self.client.post(url, {"email": email}) self.assertEqual(response.status_code, 404) - @override_settings(EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend') - @patch('des.views.send_mail', return_value = 1) + @override_settings(EMAIL_BACKEND="django.core.mail.backends.dummy.EmailBackend") + @patch("des.views.send_mail", return_value=1) def test_send_test_email_returns_error_for_send_mail_exception(self, send_mail): send_mail.side_effect = SMTPException() - url = reverse('des-test-email') - email = 'testemail@website.com' - response = self.client.post(url, { - 'email': email - }) + url = reverse("des-test-email") + email = "testemail@website.com" + response = self.client.post(url, {"email": email}) self.assertIsNotNone(response.wsgi_request._messages) messages = list(response.wsgi_request._messages) - self.assertTrue('messages' in response.cookies) + self.assertTrue("messages" in response.cookies) self.assertEqual(len(messages), 1) - self.assertIn('Could not send email', messages[0].message) + self.assertIn("Could not send email", messages[0].message) - @override_settings(EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend') + @override_settings(EMAIL_BACKEND="django.core.mail.backends.dummy.EmailBackend") def test_send_test_email_returns_error_for_no_email(self): - url = reverse('des-test-email') + url = reverse("des-test-email") response = self.client.post(url, {}) self.assertIsNotNone(response.wsgi_request._messages) messages = list(response.wsgi_request._messages) - self.assertTrue('messages' in response.cookies) + self.assertTrue("messages" in response.cookies) self.assertEqual(len(messages), 1) - self.assertIn('You must provide an email address', messages[0].message) + self.assertIn("You must provide an email address", messages[0].message) - @override_settings(EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend') - @patch('des.views.send_mail', return_value = 1) + @override_settings(EMAIL_BACKEND="django.core.mail.backends.dummy.EmailBackend") + @patch("des.views.send_mail", return_value=1) def test_send_test_email_does_not_invoke_send_mail_for_empty_email(self, send_mail): send_mail.side_effect = SMTPException() - url = reverse('des-test-email') + url = reverse("des-test-email") self.client.post(url, {}) self.assertEqual(send_mail.call_count, 0) diff --git a/tests/urls.py b/tests/urls.py index 833bec2..66d189b 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,12 +1,9 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, absolute_import +from __future__ import absolute_import, unicode_literals -from django.conf.urls import url, include from django.contrib import admin -from des import urls as des_urls +from django.urls import include, path +from des import urls as des_urls -urlpatterns = [ - url(r'^admin/', admin.site.urls), - url(r'^django-des/', include(des_urls)) -] +urlpatterns = [path("admin/", admin.site.urls), path("django-des/", include(des_urls))] diff --git a/tox.ini b/tox.ini index 2bed5a2..d400a1f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,25 +1,21 @@ [tox] envlist = - {py27,py34,py35}-django-18 - {py27,py34,py35}-django-19 - {py27,py34,py35}-django-110 - {py27,py34,py35,py36}-django-111 - {py34,py35,py36}-django-20 + {py36,py37,py38}-django-30 + {py38,py39,py310}-django-40 + {py310,py311}-django-50 [testenv] setenv = PYTHONPATH = {toxinidir}:{toxinidir}/des commands = coverage run --source des runtests.py deps = - django-18: Django>=1.8,<1.9 - django-19: Django>=1.9,<1.10 - django-110: Django>=1.10,<1.11 - django-111: Django>=1.11,<2.0 - django-20: Django>=2.0,<2.1 + django-30: Django>=3.0,<4.1 + django-40: Django>=4.0,<5.1 + django-50: Django>=5.0,<6.1 -r{toxinidir}/requirements_test.txt -r{toxinidir}/requirements.txt basepython = - py36: python3.6 - py35: python3.5 - py34: python3.4 - py27: python2.7 + py311: python3.11 + py310: python3.10 + py39: python3.9 + py38: python3.8