Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-django-mailer for 
openSUSE:Factory checked in at 2021-12-26 13:30:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-mailer (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-mailer.new.2520 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-mailer"

Sun Dec 26 13:30:38 2021 rev:2 rq:942593 version:2.1

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-django-mailer/python-django-mailer.changes    
    2020-07-08 19:14:19.095387900 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-django-mailer.new.2520/python-django-mailer.changes
      2021-12-26 13:30:58.430978921 +0100
@@ -1,0 +2,18 @@
+Sun Dec 26 11:31:52 UTC 2021 - John Vandenberg <jay...@gmail.com>
+
+- Update to v2.1
+  * The retry_deferred and send_mail commands rely on the log level
+    set in your django project now. The -c/--cron option in those
+    commands has been deprecated and the logic to configure log
+    levels and the message format has been removed.
+  * Changed logging to use module specific loggers to avoid
+    interfering with other loggers.
+  * Added MAILER_USE_FILE_LOCK setting to allow disabling file based
+    locking.
+  * Added -r option to purge_mail_log management command.
+  * Fixed deprecation warnings on Django 3.1
+  * Use cached DNS_NAME for performance
+  * Added ability to override the default error handler via the
+    MAILER_ERROR_HANDLER settings key
+
+-------------------------------------------------------------------

Old:
----
  django-mailer-2.0.1.tar.gz

New:
----
  django-mailer-2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-mailer.spec ++++++
--- /var/tmp/diff_new_pack.1dciVg/_old  2021-12-26 13:30:58.838979205 +0100
+++ /var/tmp/diff_new_pack.1dciVg/_new  2021-12-26 13:30:58.842979208 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-django-mailer
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-django-mailer
-Version:        2.0.1
+Version:        2.1
 Release:        0
 Summary:        A reusable Django app for queuing the sending of email
 License:        MIT
@@ -60,6 +60,6 @@
 %files %{python_files}
 %doc AUTHORS CHANGES.rst README.rst
 %license LICENSE
-%{python_sitelib}/*
+%{python_sitelib}/*mailer*/
 
 %changelog

++++++ django-mailer-2.0.1.tar.gz -> django-mailer-2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/.travis.yml 
new/django-mailer-2.1/.travis.yml
--- old/django-mailer-2.0.1/.travis.yml 2019-09-23 19:24:14.000000000 +0200
+++ new/django-mailer-2.1/.travis.yml   2020-10-11 19:47:09.000000000 +0200
@@ -31,8 +31,10 @@
     env: TOXENV=py36-django22-test
   - python: 3.6
     env: TOXENV=py36-django30-test
-  - python: 3.6
-    env: TOXENV=py36-flake
+  - python: 3.8
+    env: TOXENV=py38-django31-test
+  - python: 3.8
+    env: TOXENV=py38-flake
 
 install:
   - pip install coveralls tox>=2.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/AUTHORS 
new/django-mailer-2.1/AUTHORS
--- old/django-mailer-2.0.1/AUTHORS     2015-07-03 13:16:58.000000000 +0200
+++ new/django-mailer-2.1/AUTHORS       2020-12-05 11:39:47.000000000 +0100
@@ -11,3 +11,5 @@
     * Jannis Leidel
     * Luke Plant
     * Renato Alves
+    * Paul Brown
+    * Sebastian Pipping
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/CHANGES.rst 
new/django-mailer-2.1/CHANGES.rst
--- old/django-mailer-2.0.1/CHANGES.rst 2020-03-01 19:48:34.000000000 +0100
+++ new/django-mailer-2.1/CHANGES.rst   2020-12-05 11:54:27.000000000 +0100
@@ -1,6 +1,22 @@
 Change log
 ==========
 
+2.1 - 2020-12-05
+----------------
+
+* The ``retry_deferred`` and ``send_mail`` commands rely on the log level set
+  in your django project now. The ``-c/--cron`` option in those commands has
+  been deprecated and the logic to configure log levels and the message
+  format has been removed.
+* Changed logging to use module specific loggers to avoid interfering
+  with other loggers.
+* Added ``MAILER_USE_FILE_LOCK`` setting to allow disabling file based locking.
+* Added ``-r`` option to ``purge_mail_log`` management command. Thanks 
julienc91
+* Fixed deprecation warnings on Django 3.1
+* Use cached DNS_NAME for performance
+* Added ability to override the default error handler via the 
``MAILER_ERROR_HANDLER``
+  settings key
+
 2.0.1 - 2020-03-01
 ------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/PKG-INFO 
new/django-mailer-2.1/PKG-INFO
--- old/django-mailer-2.0.1/PKG-INFO    2020-03-01 20:25:48.000000000 +0100
+++ new/django-mailer-2.1/PKG-INFO      2020-12-05 11:56:51.692757600 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-mailer
-Version: 2.0.1
+Version: 2.1
 Summary: A reusable Django app for queuing the sending of email
 Home-page: http://github.com/pinax/django-mailer/
 Author: Pinax Team
@@ -82,7 +82,8 @@
         * ``retry_deferred`` will move any deferred mail back into the normal 
queue
           (so it will be attempted again on the next ``send_mail``).
         
-        * ``purge_mail_log`` will remove old successful message logs from the 
database, to prevent it from filling up your database
+        * ``purge_mail_log`` will remove old successful message logs from the 
database, to prevent it from filling up your database.
+          Use the ``-r failure`` option to remove only failed message logs 
instead, or ``-r all`` to remove them all.
         
         
         You may want to set these up via cron to run regularly::
@@ -129,6 +130,60 @@
         
         Unprocessed emails will be evaluated in the following delivery 
iterations.
         
+        Error handling
+        ==============
+        
+        django-mailer comes with a default error handler
+        ``mailer.engine.handle_delivery_exception``.
+        
+        It marks the related message as deferred for any of these exceptions:
+        
+        - ``smtplib.SMTPAuthenticationError``
+        - ``smtplib.SMTPDataError``
+        - ``smtplib.SMTPRecipientsRefused``
+        - ``smtplib.SMTPSenderRefused``
+        - ``socket.error``
+        
+        Any other exceptions is re-raised.
+        That is done for backwords-compatiblity as well as for flexibility:
+        we would otherwise have to maintain an extensive and changing
+        list of exception types, which does not scale, and you get
+        the chance to do error handling that fits your environment like a 
glove.
+        
+        When the default behavior does not fit your environment, you can 
specify your
+        own custom delivery error handler through setting 
``MAILER_ERROR_HANDLER``.
+        The value should be a string for use with Django's ``import_string``,
+        the default is ``"mailer.engine.handle_delivery_exception"``.
+        
+        Your handler is passed three arguments, in order:
+        
+        - ``connection`` ??? the backend connection instance that failed 
delivery
+        - ``message`` ??? the ``Message`` instance that failed delivery
+        - ``exc`` ??? the exception instance raised by the mailer backend
+        
+        Your handler should return a 2-tuple of:
+        
+        1. a connection instance (or ``None`` to cause a new connection to be 
created)
+        2. a string denoting the action taken by the handler,
+           either ``"sent"`` or ``"deferred"`` precisely
+        
+        For an example of a custom error handler::
+        
+            def my_handler(connection, message, exc):
+                if isinstance(exc, SomeDeliveryException):
+                    # trying to re-send this very message desparately
+                    # (if you have good reason to)
+                    [..]
+                    status = 'sent'
+                elif isinstance(exc, SomeOtherException):
+                    message.defer()
+                    connection = None  # i.e. ask for a new connection
+                    status = 'deferred'
+                else:
+                    six.reraise(*sys.exc_info())
+        
+                return connection, status
+        
         Other settings
         ==============
         
@@ -139,6 +194,9 @@
         delete this file, and others in the same directory. With the default 
value of
         ``None`` django-mailer will use a path in current working directory.
         
+        If you need to disable the file-based locking, you can set the
+        ``MAILER_USE_FILE_LOCK`` setting to ``False``.
+        
         If you need to change the batch size used by django-mailer to save 
messages in
         ``mailer.backend.DbBackend``, you can set 
``MAILER_MESSAGES_BATCH_SIZE`` to a
         value more suitable for you. This value, which defaults to `None`, 
will be passed to
@@ -158,6 +216,22 @@
         Change log
         ==========
         
+        2.1 - 2020-12-05
+        ----------------
+        
+        * The ``retry_deferred`` and ``send_mail`` commands rely on the log 
level set
+          in your django project now. The ``-c/--cron`` option in those 
commands has
+          been deprecated and the logic to configure log levels and the message
+          format has been removed.
+        * Changed logging to use module specific loggers to avoid interfering
+          with other loggers.
+        * Added ``MAILER_USE_FILE_LOCK`` setting to allow disabling file based 
locking.
+        * Added ``-r`` option to ``purge_mail_log`` management command. Thanks 
julienc91
+        * Fixed deprecation warnings on Django 3.1
+        * Use cached DNS_NAME for performance
+        * Added ability to override the default error handler via the 
``MAILER_ERROR_HANDLER``
+          settings key
+        
         2.0.1 - 2020-03-01
         ------------------
         
@@ -291,4 +365,7 @@
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Classifier: Framework :: Django
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/README.rst 
new/django-mailer-2.1/README.rst
--- old/django-mailer-2.0.1/README.rst  2019-09-23 19:24:14.000000000 +0200
+++ new/django-mailer-2.1/README.rst    2020-10-11 18:34:19.000000000 +0200
@@ -20,19 +20,16 @@
     :target:  https://pypi.python.org/pypi/django-mailer/
 
 
-Pinax
------
-
-Pinax is an open-source platform built on the Django Web Framework. It is an 
ecosystem of reusable Django apps, themes, and starter project templates.
-This collection can be found at http://pinaxproject.com.
-
-This app was developed as part of the Pinax ecosystem but is just a Django app 
and can be used independently of other Pinax apps.
-
-
 django-mailer
 -------------
 
 ``django-mailer`` is a reusable Django app for queuing the sending of email.
+It works by storing email in the database for later sending.
+
+Keep in mind that file attachments are also temporarily stored in the 
database, which means if you are sending files larger than several hundred KB 
in size, you are likely to run into database limitations on how large your 
query can be. If this happens, you'll either need to fall back to using 
Django's default mail backend, or increase your database limits (a procedure 
that depends on which database you are using).
+
+django-mailer was developed as part of the `Pinax ecosystem 
<http://pinaxproject.com>`_ but is just a Django app and can be used 
independently of other Pinax apps.
+
 
 Requirements
 ------------
@@ -78,29 +75,37 @@
 
     0 0 * * * (/path/to/your/python /path/to/your/manage.py purge_mail_log 7 
>> ~/cron_mail_purge.log 2>&1)
 
-Documentation
--------------
+Use the `-r failure` option to remove only failed log entries instead, or `-r 
all` to remove them all.
 
-See ``usage.rst`` in the docs for more advanced use cases - 
https://github.com/pinax/django-mailer/blob/master/docs/usage.rst#usage.
+Documentation and support
+-------------------------
+
+See `usage.rst 
<https://github.com/pinax/django-mailer/blob/master/docs/usage.rst#usage>`_
+in the docs for more advanced use cases.
 The Pinax documentation is available at http://pinaxproject.com/pinax/.
 
+This is an Open Source project maintained by volunteers, and outside this 
documentation the maintainers
+do not offer other support. For cases where you have found a bug you can file 
a GitHub issue.
+In case of any questions we recommend you join the `Pinax Slack team 
<http://slack.pinaxproject.com>`_
+and ping the Pinax team there instead of creating an issue on GitHub. You may 
also be able to get help on
+other programming sites like `Stack Overflow <https://stackoverflow.com/>`_.
+
 
 Contribute
 ----------
 
-See ``CONTRIBUTING.rst`` for information about contributing patches to 
``django-mailer``.
+See `CONTRIBUTING.rst 
<https://github.com/pinax/django-mailer/blob/master/CONTRIBUTING.rst>`_ for 
information about contributing patches to ``django-mailer``.
 
-See this blog post 
http://blog.pinaxproject.com/2016/02/26/recap-february-pinax-hangout/ including 
a video, or our How to Contribute 
(http://pinaxproject.com/pinax/how_to_contribute/) section for an overview on 
how contributing to Pinax works. For concrete contribution ideas, please see 
our Ways to Contribute/What We Need Help With 
(http://pinaxproject.com/pinax/ways_to_contribute/) section.
+See this `blog post including a video 
<http://blog.pinaxproject.com/2016/02/26/recap-february-pinax-hangout/>`_, or 
our `How to Contribute <http://pinaxproject.com/pinax/how_to_contribute/>`_ 
section for an overview on how contributing to Pinax works. For concrete 
contribution ideas, please see our `Ways to Contribute/What We Need Help With 
<http://pinaxproject.com/pinax/ways_to_contribute/>`_ section.
 
-In case of any questions we recommend you join our Pinax Slack team 
(http://slack.pinaxproject.com) and ping us there instead of creating an issue 
on GitHub. Creating issues on GitHub is of course also valid but we are usually 
able to help you faster if you ping us in Slack.
 
-We also highly recommend reading our Open Source and Self-Care blog post 
(http://blog.pinaxproject.com/2016/01/19/open-source-and-self-care/).
+We also highly recommend reading our `Open Source and Self-Care blog post 
<http://blog.pinaxproject.com/2016/01/19/open-source-and-self-care/>`_.
 
 
 Code of Conduct
 ---------------
 
-In order to foster a kind, inclusive, and harassment-free community, the Pinax 
Project has a code of conduct, which can be found here  
http://pinaxproject.com/pinax/code_of_conduct/.
+In order to foster a kind, inclusive, and harassment-free community, the Pinax 
Project has a `code of conduct 
<http://pinaxproject.com/pinax/code_of_conduct/>`_.
 We ask you to treat everyone as a smart human programmer that shares an 
interest in Python, Django, and Pinax with you.
 
 
@@ -108,4 +113,4 @@
 Pinax Project Blog and Twitter
 ------------------------------
 
-For updates and news regarding the Pinax Project, please follow us on Twitter 
at @pinaxproject and check out our blog http://blog.pinaxproject.com.
+For updates and news regarding the Pinax Project, please follow us on Twitter 
at @pinaxproject and check out `our blog <http://blog.pinaxproject.com>`_.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/docs/usage.rst 
new/django-mailer-2.1/docs/usage.rst
--- old/django-mailer-2.0.1/docs/usage.rst      2019-09-23 19:24:14.000000000 
+0200
+++ new/django-mailer-2.1/docs/usage.rst        2020-12-05 11:39:47.000000000 
+0100
@@ -74,7 +74,8 @@
 * ``retry_deferred`` will move any deferred mail back into the normal queue
   (so it will be attempted again on the next ``send_mail``).
 
-* ``purge_mail_log`` will remove old successful message logs from the 
database, to prevent it from filling up your database
+* ``purge_mail_log`` will remove old successful message logs from the 
database, to prevent it from filling up your database.
+  Use the ``-r failure`` option to remove only failed message logs instead, or 
``-r all`` to remove them all.
 
 
 You may want to set these up via cron to run regularly::
@@ -121,6 +122,60 @@
 
 Unprocessed emails will be evaluated in the following delivery iterations.
 
+Error handling
+==============
+
+django-mailer comes with a default error handler
+``mailer.engine.handle_delivery_exception``.
+
+It marks the related message as deferred for any of these exceptions:
+
+- ``smtplib.SMTPAuthenticationError``
+- ``smtplib.SMTPDataError``
+- ``smtplib.SMTPRecipientsRefused``
+- ``smtplib.SMTPSenderRefused``
+- ``socket.error``
+
+Any other exceptions is re-raised.
+That is done for backwords-compatiblity as well as for flexibility:
+we would otherwise have to maintain an extensive and changing
+list of exception types, which does not scale, and you get
+the chance to do error handling that fits your environment like a glove.
+
+When the default behavior does not fit your environment, you can specify your
+own custom delivery error handler through setting ``MAILER_ERROR_HANDLER``.
+The value should be a string for use with Django's ``import_string``,
+the default is ``"mailer.engine.handle_delivery_exception"``.
+
+Your handler is passed three arguments, in order:
+
+- ``connection`` ??? the backend connection instance that failed delivery
+- ``message`` ??? the ``Message`` instance that failed delivery
+- ``exc`` ??? the exception instance raised by the mailer backend
+
+Your handler should return a 2-tuple of:
+
+1. a connection instance (or ``None`` to cause a new connection to be created)
+2. a string denoting the action taken by the handler,
+   either ``"sent"`` or ``"deferred"`` precisely
+
+For an example of a custom error handler::
+
+    def my_handler(connection, message, exc):
+        if isinstance(exc, SomeDeliveryException):
+            # trying to re-send this very message desparately
+            # (if you have good reason to)
+            [..]
+            status = 'sent'
+        elif isinstance(exc, SomeOtherException):
+            message.defer()
+            connection = None  # i.e. ask for a new connection
+            status = 'deferred'
+        else:
+            six.reraise(*sys.exc_info())
+
+        return connection, status
+
 Other settings
 ==============
 
@@ -131,6 +186,9 @@
 delete this file, and others in the same directory. With the default value of
 ``None`` django-mailer will use a path in current working directory.
 
+If you need to disable the file-based locking, you can set the
+``MAILER_USE_FILE_LOCK`` setting to ``False``.
+
 If you need to change the batch size used by django-mailer to save messages in
 ``mailer.backend.DbBackend``, you can set ``MAILER_MESSAGES_BATCH_SIZE`` to a
 value more suitable for you. This value, which defaults to `None`, will be 
passed to
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/runtests.py 
new/django-mailer-2.1/runtests.py
--- old/django-mailer-2.0.1/runtests.py 2019-09-23 19:24:14.000000000 +0200
+++ new/django-mailer-2.1/runtests.py   2020-10-11 19:21:59.000000000 +0200
@@ -8,6 +8,7 @@
 from django.test.utils import get_runner
 
 warnings.simplefilter("always", DeprecationWarning)
+warnings.simplefilter("always", PendingDeprecationWarning)
 
 DEFAULT_SETTINGS = dict(
     INSTALLED_APPS=[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/setup.cfg 
new/django-mailer-2.1/setup.cfg
--- old/django-mailer-2.0.1/setup.cfg   2020-03-01 20:25:48.000000000 +0100
+++ new/django-mailer-2.1/setup.cfg     2020-12-05 11:56:51.692757600 +0100
@@ -3,7 +3,6 @@
 
 [flake8]
 max-line-length = 100
-max-complexity = 10
 exclude = src/mailer/migrations,build,.tox
 
 [egg_info]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/setup.py 
new/django-mailer-2.1/setup.py
--- old/django-mailer-2.0.1/setup.py    2020-03-01 19:49:12.000000000 +0100
+++ new/django-mailer-2.1/setup.py      2020-12-05 11:54:27.000000000 +0100
@@ -4,7 +4,7 @@
 
 setup(
     name="django-mailer",
-    version="2.0.1",
+    version="2.1",
     description="A reusable Django app for queuing the sending of email",
     long_description=open("docs/usage.rst").read() + 
open("CHANGES.rst").read(),
     author="Pinax Team",
@@ -24,6 +24,9 @@
         "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",
         "Framework :: Django",
     ],
     install_requires=[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-mailer-2.0.1/src/django_mailer.egg-info/PKG-INFO 
new/django-mailer-2.1/src/django_mailer.egg-info/PKG-INFO
--- old/django-mailer-2.0.1/src/django_mailer.egg-info/PKG-INFO 2020-03-01 
20:25:48.000000000 +0100
+++ new/django-mailer-2.1/src/django_mailer.egg-info/PKG-INFO   2020-12-05 
11:56:51.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-mailer
-Version: 2.0.1
+Version: 2.1
 Summary: A reusable Django app for queuing the sending of email
 Home-page: http://github.com/pinax/django-mailer/
 Author: Pinax Team
@@ -82,7 +82,8 @@
         * ``retry_deferred`` will move any deferred mail back into the normal 
queue
           (so it will be attempted again on the next ``send_mail``).
         
-        * ``purge_mail_log`` will remove old successful message logs from the 
database, to prevent it from filling up your database
+        * ``purge_mail_log`` will remove old successful message logs from the 
database, to prevent it from filling up your database.
+          Use the ``-r failure`` option to remove only failed message logs 
instead, or ``-r all`` to remove them all.
         
         
         You may want to set these up via cron to run regularly::
@@ -129,6 +130,60 @@
         
         Unprocessed emails will be evaluated in the following delivery 
iterations.
         
+        Error handling
+        ==============
+        
+        django-mailer comes with a default error handler
+        ``mailer.engine.handle_delivery_exception``.
+        
+        It marks the related message as deferred for any of these exceptions:
+        
+        - ``smtplib.SMTPAuthenticationError``
+        - ``smtplib.SMTPDataError``
+        - ``smtplib.SMTPRecipientsRefused``
+        - ``smtplib.SMTPSenderRefused``
+        - ``socket.error``
+        
+        Any other exceptions is re-raised.
+        That is done for backwords-compatiblity as well as for flexibility:
+        we would otherwise have to maintain an extensive and changing
+        list of exception types, which does not scale, and you get
+        the chance to do error handling that fits your environment like a 
glove.
+        
+        When the default behavior does not fit your environment, you can 
specify your
+        own custom delivery error handler through setting 
``MAILER_ERROR_HANDLER``.
+        The value should be a string for use with Django's ``import_string``,
+        the default is ``"mailer.engine.handle_delivery_exception"``.
+        
+        Your handler is passed three arguments, in order:
+        
+        - ``connection`` ??? the backend connection instance that failed 
delivery
+        - ``message`` ??? the ``Message`` instance that failed delivery
+        - ``exc`` ??? the exception instance raised by the mailer backend
+        
+        Your handler should return a 2-tuple of:
+        
+        1. a connection instance (or ``None`` to cause a new connection to be 
created)
+        2. a string denoting the action taken by the handler,
+           either ``"sent"`` or ``"deferred"`` precisely
+        
+        For an example of a custom error handler::
+        
+            def my_handler(connection, message, exc):
+                if isinstance(exc, SomeDeliveryException):
+                    # trying to re-send this very message desparately
+                    # (if you have good reason to)
+                    [..]
+                    status = 'sent'
+                elif isinstance(exc, SomeOtherException):
+                    message.defer()
+                    connection = None  # i.e. ask for a new connection
+                    status = 'deferred'
+                else:
+                    six.reraise(*sys.exc_info())
+        
+                return connection, status
+        
         Other settings
         ==============
         
@@ -139,6 +194,9 @@
         delete this file, and others in the same directory. With the default 
value of
         ``None`` django-mailer will use a path in current working directory.
         
+        If you need to disable the file-based locking, you can set the
+        ``MAILER_USE_FILE_LOCK`` setting to ``False``.
+        
         If you need to change the batch size used by django-mailer to save 
messages in
         ``mailer.backend.DbBackend``, you can set 
``MAILER_MESSAGES_BATCH_SIZE`` to a
         value more suitable for you. This value, which defaults to `None`, 
will be passed to
@@ -158,6 +216,22 @@
         Change log
         ==========
         
+        2.1 - 2020-12-05
+        ----------------
+        
+        * The ``retry_deferred`` and ``send_mail`` commands rely on the log 
level set
+          in your django project now. The ``-c/--cron`` option in those 
commands has
+          been deprecated and the logic to configure log levels and the message
+          format has been removed.
+        * Changed logging to use module specific loggers to avoid interfering
+          with other loggers.
+        * Added ``MAILER_USE_FILE_LOCK`` setting to allow disabling file based 
locking.
+        * Added ``-r`` option to ``purge_mail_log`` management command. Thanks 
julienc91
+        * Fixed deprecation warnings on Django 3.1
+        * Use cached DNS_NAME for performance
+        * Added ability to override the default error handler via the 
``MAILER_ERROR_HANDLER``
+          settings key
+        
         2.0.1 - 2020-03-01
         ------------------
         
@@ -291,4 +365,7 @@
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Classifier: Framework :: Django
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/src/mailer/__init__.py 
new/django-mailer-2.1/src/mailer/__init__.py
--- old/django-mailer-2.0.1/src/mailer/__init__.py      2020-03-01 
19:48:59.000000000 +0100
+++ new/django-mailer-2.1/src/mailer/__init__.py        2020-12-05 
11:54:27.000000000 +0100
@@ -2,8 +2,9 @@
 
 import warnings
 
+import six
 
-__version__ = '2.0.1'
+__version__ = '2.1'
 
 
 def get_priority(priority):
@@ -25,13 +26,17 @@
 
 def send_mail(subject, message, from_email, recipient_list, priority=None,
               fail_silently=False, auth_user=None, auth_password=None):
-    from django.utils.encoding import force_text
+    if six.PY2:
+        # Only runs Django 1.11
+        from django.utils.encoding import force_unicode as force_str
+    else:
+        from django.utils.encoding import force_str
     from mailer.models import make_message
 
     priority = get_priority(priority)
     # need to do this in case subject used lazy version of ugettext
-    subject = force_text(subject)
-    message = force_text(message)
+    subject = force_str(subject)
+    message = force_str(message)
 
     make_message(subject=subject,
                  body=message,
@@ -47,15 +52,19 @@
     """
     Function to queue HTML e-mails
     """
-    from django.utils.encoding import force_text
+    if six.PY2:
+        # Only runs Django 1.11
+        from django.utils.encoding import force_unicode as force_str
+    else:
+        from django.utils.encoding import force_str
     from django.core.mail import EmailMultiAlternatives
     from mailer.models import make_message
 
     priority = get_priority(priority)
 
     # need to do this in case subject used lazy version of ugettext
-    subject = force_text(subject)
-    message = force_text(message)
+    subject = force_str(subject)
+    message = force_str(message)
 
     msg = make_message(subject=subject,
                        body=message,
@@ -86,9 +95,13 @@
 
 def mail_admins(subject, message, fail_silently=False, connection=None, 
priority=None):
     from django.conf import settings
-    from django.utils.encoding import force_text
+    if six.PY2:
+        # Only runs Django 1.11
+        from django.utils.encoding import force_unicode as force_str
+    else:
+        from django.utils.encoding import force_str
 
-    return send_mail(settings.EMAIL_SUBJECT_PREFIX + force_text(subject),
+    return send_mail(settings.EMAIL_SUBJECT_PREFIX + force_str(subject),
                      message,
                      settings.SERVER_EMAIL,
                      [a[1] for a in settings.ADMINS])
@@ -96,9 +109,13 @@
 
 def mail_managers(subject, message, fail_silently=False, connection=None, 
priority=None):
     from django.conf import settings
-    from django.utils.encoding import force_text
+    if six.PY2:
+        # Only runs Django 1.11
+        from django.utils.encoding import force_unicode as force_str
+    else:
+        from django.utils.encoding import force_str
 
-    return send_mail(settings.EMAIL_SUBJECT_PREFIX + force_text(subject),
+    return send_mail(settings.EMAIL_SUBJECT_PREFIX + force_str(subject),
                      message,
                      settings.SERVER_EMAIL,
                      [a[1] for a in settings.MANAGERS])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/src/mailer/engine.py 
new/django-mailer-2.1/src/mailer/engine.py
--- old/django-mailer-2.0.1/src/mailer/engine.py        2020-03-01 
19:43:27.000000000 +0100
+++ new/django-mailer-2.1/src/mailer/engine.py  2020-12-05 11:39:47.000000000 
+0100
@@ -3,16 +3,21 @@
 import contextlib
 import logging
 import smtplib
+import sys
 import time
 from socket import error as socket_error
 
 import lockfile
+import six
 from django import VERSION as DJANGO_VERSION
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
 from django.core.mail import get_connection
 from django.core.mail.message import make_msgid
+from django.core.mail.utils import DNS_NAME
 from django.db import DatabaseError, NotSupportedError, OperationalError, 
transaction
+from django.utils.module_loading import import_string
+
 from mailer.models import (RESULT_FAILURE, RESULT_SUCCESS, Message, 
MessageLog, get_message_id)
 
 if DJANGO_VERSION[0] >= 2:
@@ -32,6 +37,8 @@
 # in the current working directory.
 LOCK_PATH = getattr(settings, "MAILER_LOCK_PATH", None)
 
+logger = logging.getLogger(__name__)
+
 
 def prioritize():
     """
@@ -84,7 +91,8 @@
 
 def ensure_message_id(msg):
     if get_message_id(msg) is None:
-        msg.extra_headers['Message-ID'] = make_msgid()
+        # Use cached DNS_NAME for performance
+        msg.extra_headers['Message-ID'] = make_msgid(domain=DNS_NAME)
 
 
 def _limits_reached(sent, deferred):
@@ -93,8 +101,8 @@
     EMAIL_MAX_BATCH = getattr(settings, "MAILER_EMAIL_MAX_BATCH", None)
 
     if EMAIL_MAX_BATCH is not None and sent >= EMAIL_MAX_BATCH:
-        logging.info("EMAIL_MAX_BATCH (%s) reached, "
-                     "stopping for this round", EMAIL_MAX_BATCH)
+        logger.info("EMAIL_MAX_BATCH (%s) reached, "
+                    "stopping for this round", EMAIL_MAX_BATCH)
         return True
 
     # Stop sending emails in the current round if more than X emails get
@@ -102,8 +110,8 @@
     EMAIL_MAX_DEFERRED = getattr(settings, "MAILER_EMAIL_MAX_DEFERRED", None)
 
     if EMAIL_MAX_DEFERRED is not None and deferred >= EMAIL_MAX_DEFERRED:
-        logging.warning("EMAIL_MAX_DEFERRED (%s) reached, "
-                        "stopping for this round", EMAIL_MAX_DEFERRED)
+        logger.warning("EMAIL_MAX_DEFERRED (%s) reached, "
+                       "stopping for this round", EMAIL_MAX_DEFERRED)
         return True
 
 
@@ -113,13 +121,33 @@
     EMAIL_THROTTLE = getattr(settings, "MAILER_EMAIL_THROTTLE", 0)
 
     if EMAIL_THROTTLE:
-        logging.debug("Throttling email delivery. "
-                      "Sleeping %s seconds", EMAIL_THROTTLE)
+        logger.debug("Throttling email delivery. "
+                     "Sleeping %s seconds", EMAIL_THROTTLE)
         time.sleep(EMAIL_THROTTLE)
 
 
+def handle_delivery_exception(connection, message, exc):
+    if isinstance(exc, (smtplib.SMTPAuthenticationError,
+                        smtplib.SMTPDataError,
+                        smtplib.SMTPRecipientsRefused,
+                        smtplib.SMTPSenderRefused,
+                        socket_error)):
+        message.defer()
+        logger.info("message deferred due to failure: %s" % exc)
+        MessageLog.objects.log(message, RESULT_FAILURE, log_message=str(exc))
+
+        connection = None  # i.e. enforce creation of a new connection
+        status = 'deferred'
+
+        return connection, status
+
+    # The idea is (1) to be backwards compatible with existing behavior
+    # and (2) not have delivery errors go unnoticed
+    six.reraise(*sys.exc_info())
+
+
 def acquire_lock():
-    logging.debug("acquiring lock...")
+    logger.debug("acquiring lock...")
     if LOCK_PATH is not None:
         lock_file_path = LOCK_PATH
     else:
@@ -130,19 +158,19 @@
     try:
         lock.acquire(LOCK_WAIT_TIMEOUT)
     except lockfile.AlreadyLocked:
-        logging.debug("lock already in place. quitting.")
+        logger.error("lock already in place. quitting.")
         return False, lock
     except lockfile.LockTimeout:
-        logging.debug("waiting for the lock timed out. quitting.")
+        logger.error("waiting for the lock timed out. quitting.")
         return False, lock
-    logging.debug("acquired.")
+    logger.debug("acquired.")
     return True, lock
 
 
 def release_lock(lock):
-    logging.debug("releasing lock...")
+    logger.debug("releasing lock...")
     lock.release()
-    logging.debug("released.")
+    logger.debug("released.")
 
 
 def _require_no_backend_loop(mailer_email_backend):
@@ -165,16 +193,24 @@
         "django.core.mail.backends.smtp.EmailBackend"
     )
 
+    # allows disabling file locking. The default is True
+    use_file_lock = getattr(settings, "MAILER_USE_FILE_LOCK", True)
+
+    error_handler = import_string(
+        getattr(settings, 'MAILER_ERROR_HANDLER',
+                'mailer.engine.handle_delivery_exception')
+    )
+
     _require_no_backend_loop(mailer_email_backend)
 
-    acquired, lock = acquire_lock()
-    if not acquired:
-        return
+    if use_file_lock:
+        acquired, lock = acquire_lock()
+        if not acquired:
+            return
 
     start_time = time.time()
 
-    deferred = 0
-    sent = 0
+    counts = {'deferred': 0, 'sent': 0}
 
     try:
         connection = None
@@ -186,7 +222,7 @@
                 try:
                     if connection is None:
                         connection = 
get_connection(backend=mailer_email_backend)
-                    logging.info("sending message '{0}' to {1}".format(
+                    logger.info("sending message '{0}' to {1}".format(
                         message.subject,
                         ", ".join(message.to_addresses))
                     )
@@ -200,34 +236,28 @@
                         email.connection = None
                         message.email = email  # For the sake of MessageLog
                         MessageLog.objects.log(message, RESULT_SUCCESS)
-                        sent += 1
+                        counts['sent'] += 1
                     else:
-                        logging.warning("message discarded due to failure in 
converting from DB. Added on '%s' with priority '%s'" % (message.when_added, 
message.priority))  # noqa
+                        logger.warning("message discarded due to failure in 
converting from DB. Added on '%s' with priority '%s'" % (message.when_added, 
message.priority))  # noqa
                     message.delete()
 
-                except (socket_error, smtplib.SMTPSenderRefused,
-                        smtplib.SMTPRecipientsRefused,
-                        smtplib.SMTPDataError,
-                        smtplib.SMTPAuthenticationError) as err:
-                    message.defer()
-                    logging.info("message deferred due to failure: %s" % err)
-                    MessageLog.objects.log(message, RESULT_FAILURE, 
log_message=str(err))
-                    deferred += 1
-                    # Get new connection, it case the connection itself has an 
error.
-                    connection = None
+                except Exception as err:
+                    connection, action_taken = error_handler(connection, 
message, err)
+                    counts[action_taken] += 1
 
             # Check if we reached the limits for the current run
-            if _limits_reached(sent, deferred):
+            if _limits_reached(counts['sent'], counts['deferred']):
                 break
 
             _throttle_emails()
 
     finally:
-        release_lock(lock)
+        if use_file_lock:
+            release_lock(lock)
 
-    logging.info("")
-    logging.info("%s sent; %s deferred;" % (sent, deferred))
-    logging.info("done in %.2f seconds" % (time.time() - start_time))
+    logger.info("")
+    logger.info("%s sent; %s deferred;" % (counts['sent'], counts['deferred']))
+    logger.info("done in %.2f seconds" % (time.time() - start_time))
 
 
 def send_loop():
@@ -238,6 +268,6 @@
 
     while True:
         while not Message.objects.all():
-            logging.debug("sleeping for %s seconds before checking queue 
again" % EMPTY_QUEUE_SLEEP)
+            logger.debug("sleeping for %s seconds before checking queue again" 
% EMPTY_QUEUE_SLEEP)
             time.sleep(EMPTY_QUEUE_SLEEP)
         send_all()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-mailer-2.0.1/src/mailer/management/commands/purge_mail_log.py 
new/django-mailer-2.1/src/mailer/management/commands/purge_mail_log.py
--- old/django-mailer-2.0.1/src/mailer/management/commands/purge_mail_log.py    
2019-09-23 19:24:14.000000000 +0200
+++ new/django-mailer-2.1/src/mailer/management/commands/purge_mail_log.py      
2020-12-05 11:39:47.000000000 +0100
@@ -1,16 +1,28 @@
 import logging
 from django.core.management.base import BaseCommand
-from mailer.models import MessageLog
+from mailer.models import MessageLog, RESULT_SUCCESS, RESULT_FAILURE
+
+RESULT_CODES = {
+    'success': [RESULT_SUCCESS],
+    'failure': [RESULT_FAILURE],
+    'all': [RESULT_SUCCESS, RESULT_FAILURE]
+}
+
+logger = logging.getLogger(__name__)
 
 
 class Command(BaseCommand):
     help = "Delete mailer log"
 
     def add_arguments(self, parser):
-        parser.add_argument('days', nargs=1, type=int)
+        parser.add_argument('days', type=int)
+        parser.add_argument('-r', '--result', choices=RESULT_CODES.keys(),
+                            help='Delete logs of messages with the given 
result code(s) '
+                                 '(default: success)')
 
     def handle(self, *args, **options):
-        # Compatiblity with Django-1.6
-        days = int(options.get('days', args)[0])
-        count = MessageLog.objects.purge_old_entries(days)
-        logging.info("%s log entries deleted " % count)
+        days = options['days']
+        result_codes = RESULT_CODES.get(options['result'])
+
+        count = MessageLog.objects.purge_old_entries(days, result_codes)
+        logger.info("%s log entries deleted " % count)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-mailer-2.0.1/src/mailer/management/commands/retry_deferred.py 
new/django-mailer-2.1/src/mailer/management/commands/retry_deferred.py
--- old/django-mailer-2.0.1/src/mailer/management/commands/retry_deferred.py    
2019-09-23 19:24:14.000000000 +0200
+++ new/django-mailer-2.1/src/mailer/management/commands/retry_deferred.py      
2020-12-05 11:39:47.000000000 +0100
@@ -1,18 +1,21 @@
 import logging
+import warnings
 
 from django.core.management.base import BaseCommand
 
 from mailer.models import Message
 from mailer.management.helpers import CronArgMixin
 
+logger = logging.getLogger(__name__)
+
 
 class Command(CronArgMixin, BaseCommand):
     help = "Attempt to resend any deferred mail."
 
     def handle(self, *args, **options):
-        if options['cron'] == 0:
-            logging.basicConfig(level=logging.DEBUG, format="%(message)s")
-        else:
-            logging.basicConfig(level=logging.ERROR, format="%(message)s")
+        if options['cron']:
+            warnings.warn("retry_deferred's -c/--cron option is no longer "
+                          "necessary and will be removed in a future release",
+                          DeprecationWarning)
         count = Message.objects.retry_deferred()  # @@@ new_priority not yet 
supported
-        logging.info("%s message(s) retried" % count)
+        logger.info("%s message(s) retried" % count)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-mailer-2.0.1/src/mailer/management/commands/send_mail.py 
new/django-mailer-2.1/src/mailer/management/commands/send_mail.py
--- old/django-mailer-2.0.1/src/mailer/management/commands/send_mail.py 
2019-09-23 19:24:14.000000000 +0200
+++ new/django-mailer-2.1/src/mailer/management/commands/send_mail.py   
2020-12-05 11:39:47.000000000 +0100
@@ -1,4 +1,5 @@
 import logging
+import warnings
 
 from django.conf import settings
 from django.core.management.base import BaseCommand
@@ -10,18 +11,20 @@
 # allow a sysadmin to pause the sending of mail temporarily.
 PAUSE_SEND = getattr(settings, "MAILER_PAUSE_SEND", False)
 
+logger = logging.getLogger(__name__)
+
 
 class Command(CronArgMixin, BaseCommand):
     help = "Do one pass through the mail queue, attempting to send all mail."
 
     def handle(self, *args, **options):
         if options['cron'] == 0:
-            logging.basicConfig(level=logging.DEBUG, format="%(message)s")
-        else:
-            logging.basicConfig(level=logging.ERROR, format="%(message)s")
-        logging.info("-" * 72)
+            warnings.warn("send_mail's -c/--cron option is no longer "
+                          "necessary and will be removed in a future release",
+                          DeprecationWarning)
+        logger.info("-" * 72)
         # if PAUSE_SEND is turned on don't do anything.
         if not PAUSE_SEND:
             send_all()
         else:
-            logging.info("sending is paused, quitting.")
+            logger.info("sending is paused, quitting.")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/src/mailer/models.py 
new/django-mailer-2.1/src/mailer/models.py
--- old/django-mailer-2.0.1/src/mailer/models.py        2019-09-23 
19:24:14.000000000 +0200
+++ new/django-mailer-2.1/src/mailer/models.py  2020-12-05 11:39:47.000000000 
+0100
@@ -5,6 +5,8 @@
 import pickle
 import datetime
 
+import six
+
 try:
     from django.utils.encoding import python_2_unicode_compatible
 except ImportError:
@@ -13,7 +15,10 @@
 from django.utils.timezone import now as datetime_now
 from django.core.mail import EmailMessage
 from django.db import models
-from django.utils.translation import ugettext_lazy as _
+if six.PY2:
+    from django.utils.translation import ugettext_lazy as _
+else:
+    from django.utils.translation import gettext_lazy as _
 
 
 PRIORITY_HIGH = 1
@@ -30,6 +35,8 @@
 
 PRIORITY_MAPPING = dict((label, v) for (v, label) in PRIORITIES)
 
+logger = logging.getLogger(__name__)
+
 
 def get_message_id(msg):
     # From django.core.mail.message: Email header names are case-insensitive
@@ -108,6 +115,9 @@
 
 @python_2_unicode_compatible
 class Message(models.Model):
+    """
+    The email stored for later sending.
+    """
 
     # The actual data - a pickled EmailMessage
     message_data = models.TextField()
@@ -168,7 +178,7 @@
     retval = []
     for e in lst:
         if DontSendEntry.objects.has_address(e):
-            logging.info("skipping email to %s as on don't send list " % 
e.encode("utf-8"))
+            logger.info("skipping email to %s as on don't send list " % 
e.encode("utf-8"))
         else:
             retval.append(e)
     return retval
@@ -251,9 +261,12 @@
             log_message=log_message,
         )
 
-    def purge_old_entries(self, days):
+    def purge_old_entries(self, days, result_codes=None):
+        if result_codes is None:
+            # retro-compatibility with previous versions
+            result_codes = [RESULT_SUCCESS]
         limit = datetime_now() - datetime.timedelta(days=days)
-        query = self.filter(when_attempted__lt=limit, result=RESULT_SUCCESS)
+        query = self.filter(when_attempted__lt=limit, result__in=result_codes)
         count = query.count()
         query.delete()
         return count
@@ -261,6 +274,10 @@
 
 @python_2_unicode_compatible
 class MessageLog(models.Model):
+    """
+    A log entry which stores the result (and optionally a log message) for an
+    attempt to send a Message.
+    """
 
     # fields from Message
     message_data = models.TextField()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/tests/test_mailer.py 
new/django-mailer-2.1/tests/test_mailer.py
--- old/django-mailer-2.0.1/tests/test_mailer.py        2020-03-01 
20:21:00.000000000 +0100
+++ new/django-mailer-2.1/tests/test_mailer.py  2020-12-05 11:39:47.000000000 
+0100
@@ -12,7 +12,7 @@
 from django.core.management import call_command
 from django.test import TestCase
 from django.utils.timezone import now as datetime_now
-from mock import ANY, Mock, patch
+from mock import Mock, patch
 import six
 
 import mailer
@@ -90,20 +90,20 @@
             self.assertEqual(Message.objects.count(), 0)
 
     def test_purge_old_entries(self):
-        # Send one successfully
-        with 
self.settings(MAILER_EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend"):
-            mailer.send_mail("Subject", "Body", "send...@example.com",
-                             ["recipi...@example.com"])
-            engine.send_all()
-
-        # And one failure
-        with 
self.settings(MAILER_EMAIL_BACKEND="tests.FailingMailerEmailBackend"):
-            mailer.send_mail("Subject", "Body", "send...@example.com",
-                             ["recipi...@example.com"])
 
-            engine.send_all()
-            Message.objects.retry_deferred()
-            engine.send_all()
+        def send_mail(success):
+            backend = ("django.core.mail.backends.locmem.EmailBackend"
+                       if success else "tests.FailingMailerEmailBackend")
+            with self.settings(MAILER_EMAIL_BACKEND=backend):
+                mailer.send_mail("Subject", "Body", "sen...@example.com", 
["recipi...@example.com"])
+                engine.send_all()
+                if not success:
+                    Message.objects.retry_deferred()
+                    engine.send_all()
+
+        # 1 success, 1 failure, and purge only success
+        send_mail(True)
+        send_mail(False)
 
         with patch.object(mailer.models, 'datetime_now') as datetime_now_patch:
             datetime_now_patch.return_value = datetime_now() + 
datetime.timedelta(days=2)
@@ -112,6 +112,25 @@
         
self.assertNotEqual(MessageLog.objects.filter(result=RESULT_FAILURE).count(), 0)
         
self.assertEqual(MessageLog.objects.filter(result=RESULT_SUCCESS).count(), 0)
 
+        # 1 success, 1 failure, and purge only failures
+        send_mail(True)
+
+        with patch.object(mailer.models, 'datetime_now') as datetime_now_patch:
+            datetime_now_patch.return_value = datetime_now() + 
datetime.timedelta(days=2)
+            call_command('purge_mail_log', '1', '-r', 'failure')
+
+        
self.assertEqual(MessageLog.objects.filter(result=RESULT_FAILURE).count(), 0)
+        
self.assertNotEqual(MessageLog.objects.filter(result=RESULT_SUCCESS).count(), 0)
+
+        # 1 success, 1 failure, and purge everything
+        send_mail(False)
+
+        with patch.object(mailer.models, 'datetime_now') as datetime_now_patch:
+            datetime_now_patch.return_value = datetime_now() + 
datetime.timedelta(days=2)
+            call_command('purge_mail_log', '1', '-r', 'all')
+
+        self.assertEqual(MessageLog.objects.count(), 0)
+
     def test_send_loop(self):
         with 
self.settings(MAILER_EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend"):
             with patch("mailer.engine.send_all", side_effect=StopIteration) as 
send:
@@ -263,12 +282,12 @@
             self.assertEqual(Message.objects.deferred().count(), 0)
 
         with 
self.settings(MAILER_EMAIL_BACKEND="tests.FailingMailerEmailBackend", 
MAILER_EMAIL_MAX_DEFERRED=2):  # noqa
-            # 2 will get deferred 3 remain undeferred
-            with patch("logging.warning") as w:
+            with patch('mailer.engine.logger.warning') as mock_warning:
+                # 2 will get deferred 3 remain undeferred
                 engine.send_all()
 
-                w.assert_called_once()
-                arg = w.call_args[0][0]
+                mock_warning.assert_called_once()
+                arg = mock_warning.call_args[0][0]
                 self.assertIn("EMAIL_MAX_DEFERRED", arg)
                 self.assertIn("stopping for this round", arg)
 
@@ -537,11 +556,11 @@
 
             msg.save()
 
-            with patch("logging.warning") as w:
+            with patch('mailer.engine.logger.warning') as mock_warning:
                 engine.send_all()
 
-                w.assert_called_once()
-                arg = w.call_args[0][0]
+                mock_warning.assert_called_once()
+                arg = mock_warning.call_args[0][0]
                 self.assertIn("message discarded due to failure in converting 
from DB", arg)
 
             self.assertEqual(Message.objects.count(), 0)
@@ -641,39 +660,32 @@
         return call_command(command, cron=cron_value)
 
     # newer django; test parsing by passing argument as string
+    # --cron/c command option is deprecated
     return call_command(command, '--cron={}'.format(cron_value))
 
 
 class CommandHelperTest(TestCase):
     def test_send_mail_no_cron(self):
-        with patch('mailer.management.commands.send_mail.logging') as logging:
-            call_command('send_mail')
-            logging.basicConfig.assert_called_with(level=logging.DEBUG, 
format=ANY)
+        call_command('send_mail')
 
     def test_send_mail_cron_0(self):
-        with patch('mailer.management.commands.send_mail.logging') as logging:
-            call_command_with_cron_arg('send_mail', 0)
-            logging.basicConfig.assert_called_with(level=logging.DEBUG, 
format=ANY)
+        # deprecated
+        call_command_with_cron_arg('send_mail', 0)
 
     def test_send_mail_cron_1(self):
-        with patch('mailer.management.commands.send_mail.logging') as logging:
-            call_command_with_cron_arg('send_mail', 1)
-            logging.basicConfig.assert_called_with(level=logging.ERROR, 
format=ANY)
+        # deprecated
+        call_command_with_cron_arg('send_mail', 1)
 
     def test_retry_deferred_no_cron(self):
-        with patch('mailer.management.commands.retry_deferred.logging') as 
logging:
-            call_command('retry_deferred')
-            logging.basicConfig.assert_called_with(level=logging.DEBUG, 
format=ANY)
+        call_command('retry_deferred')
 
     def test_retry_deferred_cron_0(self):
-        with patch('mailer.management.commands.retry_deferred.logging') as 
logging:
-            call_command_with_cron_arg('retry_deferred', 0)
-            logging.basicConfig.assert_called_with(level=logging.DEBUG, 
format=ANY)
+        # deprecated
+        call_command_with_cron_arg('retry_deferred', 0)
 
     def test_retry_deferred_cron_1(self):
-        with patch('mailer.management.commands.retry_deferred.logging') as 
logging:
-            call_command_with_cron_arg('retry_deferred', 1)
-            logging.basicConfig.assert_called_with(level=logging.ERROR, 
format=ANY)
+        # deprecated
+        call_command_with_cron_arg('retry_deferred', 1)
 
 
 class EmailBackendSettingLoopTest(TestCase):
@@ -686,3 +698,32 @@
         self.assertIn('mailer.backend.DbBackend', str(catcher.exception))
         self.assertIn('EMAIL_BACKEND', str(catcher.exception))
         self.assertIn('MAILER_EMAIL_BACKEND', str(catcher.exception))
+
+
+class UseFileLockTest(TestCase):
+    """Test the MAILER_USE_FILE_LOCK setting."""
+
+    def setUp(self):
+        # mocking return_value to prevent "ValueError: not enough values to 
unpack"
+        self.patcher_acquire_lock = patch("mailer.engine.acquire_lock", 
return_value=(True, True))
+        self.patcher_release_lock = patch("mailer.engine.release_lock", 
return_value=(True, True))
+
+        self.mock_acquire_lock = self.patcher_acquire_lock.start()
+        self.mock_release_lock = self.patcher_release_lock.start()
+
+    def test_mailer_use_file_lock_enabled(self):
+        with 
self.settings(MAILER_EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend"):
+            engine.send_all()
+        self.mock_acquire_lock.assert_called_once()
+        self.mock_release_lock.assert_called_once()
+
+    def test_mailer_use_file_lock_disabled(self):
+        with self.settings(MAILER_USE_FILE_LOCK=False,
+                           
MAILER_EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend"):
+            engine.send_all()
+        self.mock_acquire_lock.assert_not_called()
+        self.mock_release_lock.assert_not_called()
+
+    def tearDown(self):
+        self.patcher_acquire_lock.stop()
+        self.patcher_release_lock.stop()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-mailer-2.0.1/tox.ini 
new/django-mailer-2.1/tox.ini
--- old/django-mailer-2.0.1/tox.ini     2020-03-01 20:06:59.000000000 +0100
+++ new/django-mailer-2.1/tox.ini       2020-12-05 11:54:27.000000000 +0100
@@ -4,8 +4,9 @@
     py27-django111-test,
     py34-django{111,20}-test,
     {py35,py36}-django{111,20}-test,
-    py36-django{21,22,30}-test,
-    {py27,py34,py35,py36}-flake,
+    py38-django{21,22,30,31}-test,
+    py39-django31-test,
+    {py27,py38}-flake,
     checkmanifest,
 
 [testenv]
@@ -14,6 +15,10 @@
     py34: python3.4
     py35: python3.5
     py36: python3.6
+    py37: python3.7
+    py38: python3.8
+    py39: python3.9
+
 commands =
     test: coverage run ./runtests.py
     flake: flake8 --statistics --benchmark
@@ -25,12 +30,16 @@
     py34: mock==3.0.5
     py35: mock==3.0.5
     py36: mock==4.0.1
-    django111: Django==1.11.28
+    py37: mock==4.0.1
+    py38: mock==4.0.1
+    py39: mock==4.0.1
+    django111: Django==1.11.29
     django20: Django==2.0.13
     django21: Django==2.1.15
-    django22: Django==2.2.10
-    django30: Django==3.0.3
-    flake: flake8==3.7.7
+    django22: Django==2.2.16
+    django30: Django==3.0.10
+    django31: Django==3.1.2
+    flake: flake8==3.8.4
     py27-flake: Django<2.0
 
 [testenv:checkmanifest]

Reply via email to