Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pilkit for openSUSE:Factory 
checked in at 2023-10-06 21:13:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pilkit (Old)
 and      /work/SRC/openSUSE:Factory/.python-pilkit.new.28202 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pilkit"

Fri Oct  6 21:13:59 2023 rev:7 rq:1115887 version:3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pilkit/python-pilkit.changes      
2022-04-06 21:52:52.578618926 +0200
+++ /work/SRC/openSUSE:Factory/.python-pilkit.new.28202/python-pilkit.changes   
2023-10-06 21:16:42.872778536 +0200
@@ -1,0 +2,16 @@
+Thu Oct  5 08:36:31 UTC 2023 - Markéta Machová <mmach...@suse.com>
+
+- Update to 3.0
+  * Create Convert processor and GaussianBlur processor
+  * Updating histogram entropy implementations
+  * Make processors.Resize more memory-efficient
+  * Added WebP as a transparency format
+  * Use pytest instead of nose
+  * Make it compatible with Pillow10
+  * test: use unittest.mock instead of mock
+- Drop merged patches:
+  * switch-to-pytest.patch
+  * python-pilkit-no-mock.patch
+  * pil-fix-test.patch
+
+-------------------------------------------------------------------

Old:
----
  pil-fix-test.patch
  pilkit-2.0.tar.gz
  python-pilkit-no-mock.patch
  switch-to-pytest.patch

New:
----
  pilkit-3.0.tar.gz

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

Other differences:
------------------
++++++ python-pilkit.spec ++++++
--- /var/tmp/diff_new_pack.TMneNW/_old  2023-10-06 21:16:44.020820012 +0200
+++ /var/tmp/diff_new_pack.TMneNW/_new  2023-10-06 21:16:44.020820012 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-pilkit
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,18 +16,14 @@
 #
 
 
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%{?sle15_python_module_pythons}
 Name:           python-pilkit
-Version:        2.0
+Version:        3.0
 Release:        0
 Summary:        A collection of utilities and processors for the Python 
Imaging Libary
 License:        BSD-3-Clause
 URL:            https://github.com/matthewwithanm/pilkit/
 Source:         
https://files.pythonhosted.org/packages/source/p/pilkit/pilkit-%{version}.tar.gz
-Patch0:         pil-fix-test.patch
-Patch1:         switch-to-pytest.patch
-# https://github.com/matthewwithanm/pilkit/issues/54
-Patch2:         python-pilkit-no-mock.patch
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
@@ -36,6 +32,7 @@
 BuildRequires:  %{python_module Pillow}
 BuildRequires:  %{python_module pytest}
 # /SECTION
+Requires:       python-Pillow
 %python_subpackages
 
 %description
@@ -62,5 +59,6 @@
 %files %{python_files}
 %license LICENSE
 %doc AUTHORS README.rst
-%{python_sitelib}/*
+%{python_sitelib}/pilkit
+%{python_sitelib}/pilkit-%{version}*-info
 

++++++ pilkit-2.0.tar.gz -> pilkit-3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/PKG-INFO new/pilkit-3.0/PKG-INFO
--- old/pilkit-2.0/PKG-INFO     2017-02-17 13:07:49.000000000 +0100
+++ new/pilkit-3.0/PKG-INFO     2023-09-28 00:01:05.112009800 +0200
@@ -1,123 +1,125 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: pilkit
-Version: 2.0
-Summary: A collection of utilities and processors for the Python Imaging 
Libary.
+Version: 3.0
+Summary: A collection of utilities and processors for the Python Imaging 
Library.
 Home-page: http://github.com/matthewwithanm/pilkit/
 Author: Matthew Tretter
 Author-email: m...@tthewwithanm.com
 License: BSD
-Description: PILKit is a collection of utilities for working with PIL (the 
Python Imaging
-        Library).
-        
-        One of its main features is a set of **processors** which expose a 
simple
-        interface for performing manipulations on PIL images.
-        
-        Looking for more advanced processors? Check out `Instakit`_!
-        
-        **For the complete documentation on the latest stable version of 
PILKit, see**
-        `PILKit on RTD`_.
-        
-        .. image:: https://api.travis-ci.org/matthewwithanm/pilkit.png
-          :target: https://travis-ci.org/matthewwithanm/pilkit
-        
-        .. _`PILKit on RTD`: http://pilkit.readthedocs.org
-        .. _`Instakit`: https://github.com/fish2000/instakit
-        
-        
-        Installation
-        ============
-        
-        1. Install `PIL`_ or `Pillow`_.
-        2. Run ``pip install pilkit`` (or clone the source and put the pilkit 
module on
-           your path)
-        
-        .. note:: If you've never seen Pillow before, it considers itself a
-           more-frequently updated "friendly" fork of PIL that's compatible 
with
-           setuptools. As such, it shares the same namespace as PIL does and 
is a
-           drop-in replacement.
-        
-        .. _`PIL`: http://pypi.python.org/pypi/PIL
-        .. _`Pillow`: http://pypi.python.org/pypi/Pillow
-        
-        
-        Usage Overview
-        ==============
-        
-        
-        Processors
-        ----------
-        
-        The "pilkit.processors" module contains several classes for processing 
PIL
-        images, which provide an easy to understand API:
-        
-        .. code-block:: python
-        
-            from pilkit.processors import ResizeToFit
-        
-            img = Image.open('/path/to/my/image.png')
-            processor = ResizeToFit(100, 100)
-            new_img = processor.process(img)
-        
-        A few of the included processors are:
-        
-        * ``ResizeToFit``
-        * ``ResizeToFill``
-        * ``SmartResize``
-        * ``Adjust``
-        * ``TrimBorderColor``
-        * ``Transpose``
-        
-        There's also a ``ProcessorPipeline`` class for executing processors
-        sequentially:
-        
-        .. code-block:: python
-        
-            from pilkit.processors import ProcessorPipeline, ResizeToFit, 
Adjust
-        
-            img = Image.open('/path/to/my/image.png')
-            processor = ProcessorPipeline([Adjust(color=0), ResizeToFit(100, 
100)])
-            new_image = processor.process(img)
-        
-        
-        Utilities
-        ---------
-        
-        In addition to the processors, PILKit contains a few utilities to ease 
the pain
-        of working with PIL. Some examples:
-        
-        ``prepare_image``
-            Prepares the image for saving to the provided format by doing some
-            common-sense conversions, including preserving transparency and 
quantizing.
-        ``save_image``
-            Wraps PIL's ``Image.save()`` method in order to gracefully handle 
PIL's
-            "Suspension not allowed here" errors, and (optionally) prepares 
the image
-            using ``prepare_image``
-        
-        Utilities are also included for converting between formats, 
extensions, and
-        mimetypes.
-        
-        
-        Community
-        =========
-        
-        Please use `the GitHub issue tracker 
<https://github.com/matthewwithanm/pilkit/issues>`_
-        to report bugs. `A mailing list 
<https://groups.google.com/forum/#!forum/django-imagekit>`_
-        also exists to discuss the project and ask questions, as well as the 
official
-        `#imagekit <irc://irc.freenode.net/imagekit>`_ channel on Freenode. 
(Both of
-        these are shared with the `django-imagekit`_ project—from which 
PILKit spun
-        off.)
-        
-        .. _`django-imagekit`: https://github.com/jdriscoll/django-imagekit
-        
-Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-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: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
 Classifier: Topic :: Utilities
+License-File: LICENSE
+License-File: AUTHORS
+Requires-Dist: Pillow>=7.0
+
+PILKit is a collection of utilities for working with PIL (the Python Imaging
+Library).
+
+One of its main features is a set of **processors** which expose a simple
+interface for performing manipulations on PIL images.
+
+Looking for more advanced processors? Check out `Instakit`_!
+
+**For the complete documentation on the latest stable version of PILKit, see**
+`PILKit on RTD`_.
+
+.. image:: 
https://github.com/matthewwithanm/pilkit/workflows/Python%20CI/badge.svg
+  :target: 
https://github.com/matthewwithanm/pilkit/actions?query=workflow%3A%22Python+CI%22
+
+.. _`PILKit on RTD`: http://pilkit.readthedocs.org
+.. _`Instakit`: https://github.com/fish2000/instakit
+
+
+Installation
+============
+
+1. Install `PIL`_ or `Pillow`_.
+2. Run ``pip install pilkit`` (or clone the source and put the pilkit module on
+   your path)
+
+.. note:: If you've never seen Pillow before, it considers itself a
+   more-frequently updated "friendly" fork of PIL that's compatible with
+   setuptools. As such, it shares the same namespace as PIL does and is a
+   drop-in replacement.
+
+.. _`PIL`: http://pypi.python.org/pypi/PIL
+.. _`Pillow`: http://pypi.python.org/pypi/Pillow
+
+
+Usage Overview
+==============
+
+
+Processors
+----------
+
+The "pilkit.processors" module contains several classes for processing PIL
+images, which provide an easy to understand API:
+
+.. code-block:: python
+
+    from pilkit.processors import ResizeToFit
+
+    img = Image.open('/path/to/my/image.png')
+    processor = ResizeToFit(100, 100)
+    new_img = processor.process(img)
+
+A few of the included processors are:
+
+* ``ResizeToFit``
+* ``ResizeToFill``
+* ``SmartResize``
+* ``Adjust``
+* ``TrimBorderColor``
+* ``Transpose``
+
+There's also a ``ProcessorPipeline`` class for executing processors
+sequentially:
+
+.. code-block:: python
+
+    from pilkit.processors import ProcessorPipeline, ResizeToFit, Adjust
+
+    img = Image.open('/path/to/my/image.png')
+    processor = ProcessorPipeline([Adjust(color=0), ResizeToFit(100, 100)])
+    new_image = processor.process(img)
+
+
+Utilities
+---------
+
+In addition to the processors, PILKit contains a few utilities to ease the pain
+of working with PIL. Some examples:
+
+``prepare_image``
+    Prepares the image for saving to the provided format by doing some
+    common-sense conversions, including preserving transparency and quantizing.
+``save_image``
+    Wraps PIL's ``Image.save()`` method in order to gracefully handle PIL's
+    "Suspension not allowed here" errors, and (optionally) prepares the image
+    using ``prepare_image``
+
+Utilities are also included for converting between formats, extensions, and
+mimetypes.
+
+
+Community
+=========
+
+Please use `the GitHub issue tracker 
<https://github.com/matthewwithanm/pilkit/issues>`_
+to report bugs. `A mailing list 
<https://groups.google.com/forum/#!forum/django-imagekit>`_
+also exists to discuss the project and ask questions, as well as the official
+`#imagekit <irc://irc.freenode.net/imagekit>`_ channel on Freenode. (Both of
+these are shared with the `django-imagekit`_ project—from which PILKit spun
+off.)
+
+.. _`django-imagekit`: https://github.com/jdriscoll/django-imagekit
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/README.rst new/pilkit-3.0/README.rst
--- old/pilkit-2.0/README.rst   2017-02-17 12:45:57.000000000 +0100
+++ new/pilkit-3.0/README.rst   2023-09-26 22:35:02.000000000 +0200
@@ -9,8 +9,8 @@
 **For the complete documentation on the latest stable version of PILKit, see**
 `PILKit on RTD`_.
 
-.. image:: https://api.travis-ci.org/matthewwithanm/pilkit.png
-  :target: https://travis-ci.org/matthewwithanm/pilkit
+.. image:: 
https://github.com/matthewwithanm/pilkit/workflows/Python%20CI/badge.svg
+  :target: 
https://github.com/matthewwithanm/pilkit/actions?query=workflow%3A%22Python+CI%22
 
 .. _`PILKit on RTD`: http://pilkit.readthedocs.org
 .. _`Instakit`: https://github.com/fish2000/instakit
Binary files old/pilkit-2.0/docs/source/_ext/__pycache__/pil.cpython-311.pyc 
and new/pilkit-3.0/docs/source/_ext/__pycache__/pil.cpython-311.pyc differ
Binary files old/pilkit-2.0/docs/source/_ext/original.jpg and 
new/pilkit-3.0/docs/source/_ext/original.jpg differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/docs/source/_ext/pil.py 
new/pilkit-3.0/docs/source/_ext/pil.py
--- old/pilkit-2.0/docs/source/_ext/pil.py      1970-01-01 01:00:00.000000000 
+0100
+++ new/pilkit-3.0/docs/source/_ext/pil.py      2023-09-26 22:35:02.000000000 
+0200
@@ -0,0 +1,63 @@
+import os, sys, io, base64
+from docutils import nodes
+from sphinx.directives.code import CodeBlock
+
+
+def visit_pil(self, node):
+    pass
+
+
+def depart_pil(self, node):
+    pass
+
+
+class PILNode(nodes.Structural, nodes.Element):
+    pass
+
+
+class PILDirective(CodeBlock):
+    has_content = True
+
+    def run(self):
+        code_node = CodeBlock.run(self)
+        nodes.image()
+
+        from PIL import Image
+
+        lib_path = os.path.abspath('..')
+        working_path = os.path.dirname(os.path.realpath(__file__))
+        static_path = os.path.join(working_path, '..', '_static')
+
+        sys.path.append(lib_path)
+        os.chdir(working_path)
+
+        node = PILNode()
+
+        g = globals()
+        l = locals()
+        for line in self.content:
+            result = exec(line, g, l)
+
+        buffer = io.BytesIO()
+        new_img = l.get('new_img')
+        new_img.save(buffer, format='PNG')
+
+        image_node = nodes.image()
+        image_node['alt'] = 'New Image'
+        image_node['uri'] = 'data:image/png;base64,' + 
base64.b64encode(buffer.getvalue()).decode()
+
+        node += code_node
+        node += image_node
+
+        return [node]
+
+
+def setup(app):
+    app.add_node(PILNode, html=(visit_pil, depart_pil))
+    app.add_directive('pil-block', PILDirective)
+
+    return {
+        'version': '0.1',
+        'parallel_read_safe': True,
+        'parallel_write_safe': True,
+    }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/docs/source/conf.py 
new/pilkit-3.0/docs/source/conf.py
--- old/pilkit-2.0/docs/source/conf.py  2017-02-17 12:45:57.000000000 +0100
+++ new/pilkit-3.0/docs/source/conf.py  2023-09-26 22:35:02.000000000 +0200
@@ -16,7 +16,8 @@
 # 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('.'))
+sys.path.append(os.path.abspath('./_ext'))
 
 # -- General configuration 
-----------------------------------------------------
 
@@ -25,7 +26,7 @@
 
 # Add any Sphinx extension module names here, as strings. They can be 
extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
+extensions = ['pil']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/docs/source/index.rst 
new/pilkit-3.0/docs/source/index.rst
--- old/pilkit-2.0/docs/source/index.rst        2017-02-17 12:45:57.000000000 
+0100
+++ new/pilkit-3.0/docs/source/index.rst        2023-09-26 22:35:02.000000000 
+0200
@@ -5,18 +5,25 @@
 .. include:: ../../README.rst
 
 
+Contents
+--------
+
+.. toctree::
+   :maxdepth: 2
+
+   processors/resize
+   processors/filter
+
+
 Authors
-=======
+-------
 
 .. include:: ../../AUTHORS
 
 
-Contents
-=========
+Indices and tables
+------------------
 
 * :ref:`genindex`
 * :ref:`modindex`
 * :ref:`search`
-
-.. toctree::
-   :maxdepth: 2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/docs/source/processors/filter.rst 
new/pilkit-3.0/docs/source/processors/filter.rst
--- old/pilkit-2.0/docs/source/processors/filter.rst    1970-01-01 
01:00:00.000000000 +0100
+++ new/pilkit-3.0/docs/source/processors/filter.rst    2023-09-26 
22:35:02.000000000 +0200
@@ -0,0 +1,37 @@
+
+Filter
+=======
+
+Original Image
+
+.. image:: ../_ext/original.jpg
+
+
+Gaussian Blur
+-------------
+
+.. pil-block::
+
+    from pilkit.processors.filter import GaussianBlur
+
+    old_img = Image.open('original.jpg')
+    blur = GaussianBlur(1)
+    new_img = blur.process(old_img)
+
+
+.. pil-block::
+
+    from pilkit.processors.filter import GaussianBlur
+
+    old_img = Image.open('original.jpg')
+    blur = GaussianBlur(5)
+    new_img = blur.process(old_img)
+
+
+.. pil-block::
+
+    from pilkit.processors.filter import GaussianBlur
+
+    old_img = Image.open('original.jpg')
+    blur = GaussianBlur(10)
+    new_img = blur.process(old_img)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/docs/source/processors/resize.rst 
new/pilkit-3.0/docs/source/processors/resize.rst
--- old/pilkit-2.0/docs/source/processors/resize.rst    1970-01-01 
01:00:00.000000000 +0100
+++ new/pilkit-3.0/docs/source/processors/resize.rst    2023-09-26 
22:35:02.000000000 +0200
@@ -0,0 +1,115 @@
+
+Resize
+=======
+
+Original Image
+
+.. image:: ../_ext/original.jpg
+
+
+Resize To Fill
+---------------
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToFill
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToFill(300, 300)
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToFill
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToFill(600, 300)
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToFill
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToFill(300, 600)
+    new_img = resizer.process(old_img)
+
+
+Resize To Cover
+----------------
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToCover
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToCover(300, 300)  # width, height
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToCover
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToCover(600, 300)  # width, height
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToCover
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToCover(300, 600)  # width, height
+    new_img = resizer.process(old_img)
+
+
+Resize To Fit
+--------------
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToFit
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToFit(300, 300)
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToFit
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToFit(600, 300)
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import ResizeToFit
+
+    old_img = Image.open('original.jpg')
+    resizer = ResizeToFit(300, 600)
+    new_img = resizer.process(old_img)
+
+
+Thumbnail
+----------
+.. pil-block::
+
+    from pilkit.processors.resize import Thumbnail
+
+    old_img = Image.open('original.jpg')
+    resizer = Thumbnail(300, 300)
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import Thumbnail
+
+    old_img = Image.open('original.jpg')
+    resizer = Thumbnail(600, 300)
+    new_img = resizer.process(old_img)
+
+.. pil-block::
+
+    from pilkit.processors.resize import Thumbnail
+
+    old_img = Image.open('original.jpg')
+    resizer = Thumbnail(300, 600)
+    new_img = resizer.process(old_img)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/lib.py new/pilkit-3.0/pilkit/lib.py
--- old/pilkit-2.0/pilkit/lib.py        2017-02-17 12:45:57.000000000 +0100
+++ new/pilkit-3.0/pilkit/lib.py        2023-09-27 02:00:56.000000000 +0200
@@ -4,7 +4,7 @@
 # depending on the installation method used.
 try:
     from PIL import Image, ImageColor, ImageChops, ImageEnhance, ImageFile, \
-            ImageFilter, ImageDraw, ImageStat
+            ImageFilter, ImageDraw, ImageStat, ImageMode
 except ImportError:
     try:
         import Image
@@ -15,6 +15,7 @@
         import ImageFilter
         import ImageDraw
         import ImageStat
+        import ImageMode
     except ImportError:
         raise ImportError('PILKit was unable to import the Python Imaging 
Library. Please confirm it`s installed and available on your current Python 
path.')
 
@@ -33,3 +34,34 @@
     string_types = [basestring, str]
 except NameError:
     string_types = [str]
+
+
+def getattrsafe(obj, attr, fallback_attr = None):
+    """Similar to getattr but accept dotted path
+
+    The idea of this function is to pass dotted path to attribute.
+    If the path is missing then the fallback will be evaluated as dotted path 
also.
+    If the fallback is not present then the attribute error for the first path 
is thrown
+
+    The main idea of this function is for compatibility with Pillow < 10
+
+    Example::
+
+        >>> from PIL import Image
+        >>> getattrsafe(Image, 'Transpose.FLIP_HORIZONTAL', 'FLIP_HORIZONTAL')
+    """
+    names = attr.split('.')
+    res = obj
+    for i, name in enumerate(names):
+        try:
+            res = getattr(res, name)
+        except AttributeError:
+            missing = '.'.join(names[:i + 1])
+            if fallback_attr is None:
+                raise AttributeError("'{}' object has no attribute 
'{}'".format(type(obj).__name__, missing))
+            else:
+                try:
+                    return getattrsafe(obj, fallback_attr)
+                except AttributeError:
+                    raise AttributeError("'{}' object has no attribute '{}' or 
'{}'".format(type(obj).__name__, missing, fallback_attr))
+    return res
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/pkgmeta.py 
new/pilkit-3.0/pilkit/pkgmeta.py
--- old/pilkit-2.0/pilkit/pkgmeta.py    2017-02-17 12:55:50.000000000 +0100
+++ new/pilkit-3.0/pilkit/pkgmeta.py    2023-09-27 02:35:50.000000000 +0200
@@ -1,5 +1,5 @@
 __title__ = 'pilkit'
 __author__ = 'Matthew Tretter'
-__version__ = '2.0'
+__version__ = '3.0'
 __license__ = 'BSD'
 __all__ = ['__title__', '__author__', '__version__', '__license__']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/processors/__init__.py 
new/pilkit-3.0/pilkit/processors/__init__.py
--- old/pilkit-2.0/pilkit/processors/__init__.py        2017-02-17 
12:45:57.000000000 +0100
+++ new/pilkit-3.0/pilkit/processors/__init__.py        2023-09-26 
22:35:02.000000000 +0200
@@ -12,5 +12,7 @@
 
 from .base import *
 from .crop import *
+from .convert import *
+from .filter import *
 from .overlay import *
 from .resize import *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/processors/base.py 
new/pilkit-3.0/pilkit/processors/base.py
--- old/pilkit-2.0/pilkit/processors/base.py    2017-02-17 12:45:57.000000000 
+0100
+++ new/pilkit-3.0/pilkit/processors/base.py    2023-09-27 02:37:01.000000000 
+0200
@@ -1,4 +1,4 @@
-from pilkit.lib import Image, ImageColor, ImageEnhance
+from pilkit.lib import Image, ImageColor, ImageEnhance, getattrsafe
 
 
 class ProcessorPipeline(list):
@@ -80,7 +80,7 @@
         # Handle palleted images.
         img = img.convert('RGBA')
         # Copy orignial image and flip the orientation.
-        reflection = img.copy().transpose(Image.FLIP_TOP_BOTTOM)
+        reflection = img.copy().transpose(Transpose.FLIP_VERTICAL)
         # Create a new image filled with the bgcolor the same size.
         background = Image.new("RGBA", img.size, background_color)
         # Calculate our alpha mask.
@@ -116,11 +116,11 @@
 
     """
     AUTO = 'auto'
-    FLIP_HORIZONTAL = Image.FLIP_LEFT_RIGHT
-    FLIP_VERTICAL = Image.FLIP_TOP_BOTTOM
-    ROTATE_90 = Image.ROTATE_90
-    ROTATE_180 = Image.ROTATE_180
-    ROTATE_270 = Image.ROTATE_270
+    FLIP_HORIZONTAL = getattrsafe(Image, 'Transpose.FLIP_LEFT_RIGHT', 
'FLIP_LEFT_RIGHT')  # noqa
+    FLIP_VERTICAL = getattrsafe(Image, 'Transpose.FLIP_TOP_BOTTOM', 
'FLIP_TOP_BOTTOM')  # noqa
+    ROTATE_90 = getattrsafe(Image, 'Transpose.ROTATE_90', 'ROTATE_90')
+    ROTATE_180 = getattrsafe(Image, 'Transpose.ROTATE_180', 'ROTATE_180')
+    ROTATE_270 = getattrsafe(Image, 'Transpose.ROTATE_270', 'ROTATE_270')
 
     methods = [AUTO]
     _EXIF_ORIENTATION_STEPS = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/processors/convert.py 
new/pilkit-3.0/pilkit/processors/convert.py
--- old/pilkit-2.0/pilkit/processors/convert.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/pilkit-3.0/pilkit/processors/convert.py 2023-09-26 22:35:02.000000000 
+0200
@@ -0,0 +1,15 @@
+
+class Convert(object):
+    """
+    Converts image to different mode
+    """
+
+    def __init__(self, mode):
+        """
+        :param mode: Define the mode to which an image is to be converted
+
+        """
+        self.mode = mode
+
+    def process(self, img):
+        return img.convert(self.mode)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/processors/filter.py 
new/pilkit-3.0/pilkit/processors/filter.py
--- old/pilkit-2.0/pilkit/processors/filter.py  1970-01-01 01:00:00.000000000 
+0100
+++ new/pilkit-3.0/pilkit/processors/filter.py  2023-09-26 22:35:02.000000000 
+0200
@@ -0,0 +1,16 @@
+from PIL import ImageFilter
+
+class GaussianBlur(object):
+    """
+    Performs Gaussian blur filter on image.
+    """
+
+    def __init__(self, radius):
+        """
+        :param radius: Blur radius (passed to GaussianBlur filter)
+        """
+
+        self.radius = radius
+
+    def process(self, img):
+        return img.filter(ImageFilter.GaussianBlur(self.radius))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/processors/resize.py 
new/pilkit-3.0/pilkit/processors/resize.py
--- old/pilkit-2.0/pilkit/processors/resize.py  2017-02-17 12:45:57.000000000 
+0100
+++ new/pilkit-3.0/pilkit/processors/resize.py  2023-09-27 02:37:01.000000000 
+0200
@@ -1,5 +1,6 @@
 from .base import Anchor
-from ..lib import Image
+from .utils import resolve_palette
+from ..lib import Image, getattrsafe
 
 
 class Resize(object):
@@ -7,6 +8,8 @@
     Resizes an image to the specified width and height.
 
     """
+    LANCZOS = getattrsafe(Image, 'Resampling.LANCZOS', 'LANCZOS')
+
     def __init__(self, width, height, upscale=True):
         """
         :param width: The target width, in pixels.
@@ -20,8 +23,8 @@
 
     def process(self, img):
         if self.upscale or (self.width < img.size[0] and self.height < 
img.size[1]):
-            img = img.convert('RGBA')
-            img = img.resize((self.width, self.height), Image.ANTIALIAS)
+            img = resolve_palette(img)
+            img = img.resize((self.width, self.height), self.LANCZOS)
         return img
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/processors/utils.py 
new/pilkit-3.0/pilkit/processors/utils.py
--- old/pilkit-2.0/pilkit/processors/utils.py   2017-02-17 12:45:57.000000000 
+0100
+++ new/pilkit-3.0/pilkit/processors/utils.py   2023-09-27 01:01:12.000000000 
+0200
@@ -1,18 +1,32 @@
-import math
-from ..lib import Image
+from ..lib import Image, ImageMode
 
 
-def histogram_entropy(im):
+def color_count(image):
+    """ Return the number of color values in the input image --
+        this is the number of pixels times the band count
+        of the image.
     """
-    Calculate the entropy of an images' histogram. Used for "smart cropping" 
in easy-thumbnails;
-    see: 
https://raw.github.com/SmileyChris/easy-thumbnails/master/easy_thumbnails/utils.py
+    mode_descriptor = ImageMode.getmode(image.mode)
+    width, height = image.size
+    return width * height * len(mode_descriptor.bands)
 
-    """
-    if not isinstance(im, Image.Image):
-        return 0  # Fall back to a constant entropy.
+def histogram_entropy_py(image):
+    """ Calculate the entropy of an images' histogram. """
+    from math import log2, fsum
+    histosum = float(color_count(image))
+    histonorm = (histocol / histosum for histocol in image.histogram())
+    return -fsum(p * log2(p) for p in histonorm if p != 0.0)
+
+# Select the Pillow native histogram entropy function - if
+# available - and fall back to the Python implementation:
+histogram_entropy = getattr(Image.Image, 'entropy', histogram_entropy_py)
+
+def resolve_palette(image):
+    """ Convert a palette image to a non-palette image. """
 
-    histogram = im.histogram()
-    hist_ceil = float(sum(histogram))
-    histonorm = [histocol / hist_ceil for histocol in histogram]
+    # We need to load the image before accessing the palette
+    image.load()
 
-    return -sum([p * math.log(p, 2) for p in histonorm if p != 0])
+    if image.palette is None:
+        return image
+    return image.convert(image.palette.mode)
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit/utils.py 
new/pilkit-3.0/pilkit/utils.py
--- old/pilkit-2.0/pilkit/utils.py      2017-02-17 12:45:57.000000000 +0100
+++ new/pilkit-3.0/pilkit/utils.py      2023-09-27 02:00:56.000000000 +0200
@@ -3,13 +3,14 @@
 import sys
 from io import UnsupportedOperation
 from .exceptions import UnknownExtension, UnknownFormat
-from .lib import Image, ImageFile, StringIO, string_types
+from .lib import Image, ImageFile, StringIO, string_types, getattrsafe
 
 
-RGBA_TRANSPARENCY_FORMATS = ['PNG']
+RGBA_TRANSPARENCY_FORMATS = ['PNG', 'WEBP']
 PALETTE_TRANSPARENCY_FORMATS = ['PNG', 'GIF']
 DEFAULT_EXTENSIONS = {
     'JPEG': '.jpg',
+    'PNG': '.png',
 }
 
 
@@ -245,6 +246,10 @@
             # In case of Azure, the file descriptor is not present so we can 
return
             # from here
             return
+        except UnsupportedOperation:
+            # In case of Windows 2016, the file descriptor is not present so 
we can return
+            # from here
+            return
         try:
             self.null_fd = os.open(os.devnull, os.O_RDWR)
         except OSError:
@@ -306,8 +311,8 @@
 
             alpha = img.split()[-1]
             mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
-            img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE,
-                    colors=255)
+            palette = getattrsafe(Image, 'Palette.ADAPTIVE', 'ADAPTIVE')
+            img = img.convert('RGB').convert('P', palette=palette, colors=255)
             img.paste(255, mask)
             save_kwargs['transparency'] = 255
         else:
@@ -339,7 +344,8 @@
         # quantization (above). Images that are already in P mode don't need
         # any quantization because their colors are already limited.
         if format == 'GIF':
-            img = img.convert('P', palette=Image.ADAPTIVE)
+            palette = getattrsafe(Image, 'Palette.ADAPTIVE', 'ADAPTIVE')
+            img = img.convert('P', palette=palette)
 
     if make_opaque:
         from .processors import MakeOpaque
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit.egg-info/PKG-INFO 
new/pilkit-3.0/pilkit.egg-info/PKG-INFO
--- old/pilkit-2.0/pilkit.egg-info/PKG-INFO     2017-02-17 13:07:49.000000000 
+0100
+++ new/pilkit-3.0/pilkit.egg-info/PKG-INFO     2023-09-28 00:01:05.000000000 
+0200
@@ -1,123 +1,125 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: pilkit
-Version: 2.0
-Summary: A collection of utilities and processors for the Python Imaging 
Libary.
+Version: 3.0
+Summary: A collection of utilities and processors for the Python Imaging 
Library.
 Home-page: http://github.com/matthewwithanm/pilkit/
 Author: Matthew Tretter
 Author-email: m...@tthewwithanm.com
 License: BSD
-Description: PILKit is a collection of utilities for working with PIL (the 
Python Imaging
-        Library).
-        
-        One of its main features is a set of **processors** which expose a 
simple
-        interface for performing manipulations on PIL images.
-        
-        Looking for more advanced processors? Check out `Instakit`_!
-        
-        **For the complete documentation on the latest stable version of 
PILKit, see**
-        `PILKit on RTD`_.
-        
-        .. image:: https://api.travis-ci.org/matthewwithanm/pilkit.png
-          :target: https://travis-ci.org/matthewwithanm/pilkit
-        
-        .. _`PILKit on RTD`: http://pilkit.readthedocs.org
-        .. _`Instakit`: https://github.com/fish2000/instakit
-        
-        
-        Installation
-        ============
-        
-        1. Install `PIL`_ or `Pillow`_.
-        2. Run ``pip install pilkit`` (or clone the source and put the pilkit 
module on
-           your path)
-        
-        .. note:: If you've never seen Pillow before, it considers itself a
-           more-frequently updated "friendly" fork of PIL that's compatible 
with
-           setuptools. As such, it shares the same namespace as PIL does and 
is a
-           drop-in replacement.
-        
-        .. _`PIL`: http://pypi.python.org/pypi/PIL
-        .. _`Pillow`: http://pypi.python.org/pypi/Pillow
-        
-        
-        Usage Overview
-        ==============
-        
-        
-        Processors
-        ----------
-        
-        The "pilkit.processors" module contains several classes for processing 
PIL
-        images, which provide an easy to understand API:
-        
-        .. code-block:: python
-        
-            from pilkit.processors import ResizeToFit
-        
-            img = Image.open('/path/to/my/image.png')
-            processor = ResizeToFit(100, 100)
-            new_img = processor.process(img)
-        
-        A few of the included processors are:
-        
-        * ``ResizeToFit``
-        * ``ResizeToFill``
-        * ``SmartResize``
-        * ``Adjust``
-        * ``TrimBorderColor``
-        * ``Transpose``
-        
-        There's also a ``ProcessorPipeline`` class for executing processors
-        sequentially:
-        
-        .. code-block:: python
-        
-            from pilkit.processors import ProcessorPipeline, ResizeToFit, 
Adjust
-        
-            img = Image.open('/path/to/my/image.png')
-            processor = ProcessorPipeline([Adjust(color=0), ResizeToFit(100, 
100)])
-            new_image = processor.process(img)
-        
-        
-        Utilities
-        ---------
-        
-        In addition to the processors, PILKit contains a few utilities to ease 
the pain
-        of working with PIL. Some examples:
-        
-        ``prepare_image``
-            Prepares the image for saving to the provided format by doing some
-            common-sense conversions, including preserving transparency and 
quantizing.
-        ``save_image``
-            Wraps PIL's ``Image.save()`` method in order to gracefully handle 
PIL's
-            "Suspension not allowed here" errors, and (optionally) prepares 
the image
-            using ``prepare_image``
-        
-        Utilities are also included for converting between formats, 
extensions, and
-        mimetypes.
-        
-        
-        Community
-        =========
-        
-        Please use `the GitHub issue tracker 
<https://github.com/matthewwithanm/pilkit/issues>`_
-        to report bugs. `A mailing list 
<https://groups.google.com/forum/#!forum/django-imagekit>`_
-        also exists to discuss the project and ask questions, as well as the 
official
-        `#imagekit <irc://irc.freenode.net/imagekit>`_ channel on Freenode. 
(Both of
-        these are shared with the `django-imagekit`_ project—from which 
PILKit spun
-        off.)
-        
-        .. _`django-imagekit`: https://github.com/jdriscoll/django-imagekit
-        
-Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-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: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
 Classifier: Topic :: Utilities
+License-File: LICENSE
+License-File: AUTHORS
+Requires-Dist: Pillow>=7.0
+
+PILKit is a collection of utilities for working with PIL (the Python Imaging
+Library).
+
+One of its main features is a set of **processors** which expose a simple
+interface for performing manipulations on PIL images.
+
+Looking for more advanced processors? Check out `Instakit`_!
+
+**For the complete documentation on the latest stable version of PILKit, see**
+`PILKit on RTD`_.
+
+.. image:: 
https://github.com/matthewwithanm/pilkit/workflows/Python%20CI/badge.svg
+  :target: 
https://github.com/matthewwithanm/pilkit/actions?query=workflow%3A%22Python+CI%22
+
+.. _`PILKit on RTD`: http://pilkit.readthedocs.org
+.. _`Instakit`: https://github.com/fish2000/instakit
+
+
+Installation
+============
+
+1. Install `PIL`_ or `Pillow`_.
+2. Run ``pip install pilkit`` (or clone the source and put the pilkit module on
+   your path)
+
+.. note:: If you've never seen Pillow before, it considers itself a
+   more-frequently updated "friendly" fork of PIL that's compatible with
+   setuptools. As such, it shares the same namespace as PIL does and is a
+   drop-in replacement.
+
+.. _`PIL`: http://pypi.python.org/pypi/PIL
+.. _`Pillow`: http://pypi.python.org/pypi/Pillow
+
+
+Usage Overview
+==============
+
+
+Processors
+----------
+
+The "pilkit.processors" module contains several classes for processing PIL
+images, which provide an easy to understand API:
+
+.. code-block:: python
+
+    from pilkit.processors import ResizeToFit
+
+    img = Image.open('/path/to/my/image.png')
+    processor = ResizeToFit(100, 100)
+    new_img = processor.process(img)
+
+A few of the included processors are:
+
+* ``ResizeToFit``
+* ``ResizeToFill``
+* ``SmartResize``
+* ``Adjust``
+* ``TrimBorderColor``
+* ``Transpose``
+
+There's also a ``ProcessorPipeline`` class for executing processors
+sequentially:
+
+.. code-block:: python
+
+    from pilkit.processors import ProcessorPipeline, ResizeToFit, Adjust
+
+    img = Image.open('/path/to/my/image.png')
+    processor = ProcessorPipeline([Adjust(color=0), ResizeToFit(100, 100)])
+    new_image = processor.process(img)
+
+
+Utilities
+---------
+
+In addition to the processors, PILKit contains a few utilities to ease the pain
+of working with PIL. Some examples:
+
+``prepare_image``
+    Prepares the image for saving to the provided format by doing some
+    common-sense conversions, including preserving transparency and quantizing.
+``save_image``
+    Wraps PIL's ``Image.save()`` method in order to gracefully handle PIL's
+    "Suspension not allowed here" errors, and (optionally) prepares the image
+    using ``prepare_image``
+
+Utilities are also included for converting between formats, extensions, and
+mimetypes.
+
+
+Community
+=========
+
+Please use `the GitHub issue tracker 
<https://github.com/matthewwithanm/pilkit/issues>`_
+to report bugs. `A mailing list 
<https://groups.google.com/forum/#!forum/django-imagekit>`_
+also exists to discuss the project and ask questions, as well as the official
+`#imagekit <irc://irc.freenode.net/imagekit>`_ channel on Freenode. (Both of
+these are shared with the `django-imagekit`_ project—from which PILKit spun
+off.)
+
+.. _`django-imagekit`: https://github.com/jdriscoll/django-imagekit
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit.egg-info/SOURCES.txt 
new/pilkit-3.0/pilkit.egg-info/SOURCES.txt
--- old/pilkit-2.0/pilkit.egg-info/SOURCES.txt  2017-02-17 13:07:49.000000000 
+0100
+++ new/pilkit-3.0/pilkit.egg-info/SOURCES.txt  2023-09-28 00:01:05.000000000 
+0200
@@ -7,6 +7,11 @@
 docs/make.bat
 docs/source/conf.py
 docs/source/index.rst
+docs/source/_ext/original.jpg
+docs/source/_ext/pil.py
+docs/source/_ext/__pycache__/pil.cpython-311.pyc
+docs/source/processors/filter.rst
+docs/source/processors/resize.rst
 pilkit/__init__.py
 pilkit/exceptions.py
 pilkit/lib.py
@@ -16,10 +21,14 @@
 pilkit.egg-info/SOURCES.txt
 pilkit.egg-info/dependency_links.txt
 pilkit.egg-info/not-zip-safe
+pilkit.egg-info/pbr.json
+pilkit.egg-info/requires.txt
 pilkit.egg-info/top_level.txt
 pilkit/processors/__init__.py
 pilkit/processors/base.py
+pilkit/processors/convert.py
 pilkit/processors/crop.py
+pilkit/processors/filter.py
 pilkit/processors/overlay.py
 pilkit/processors/resize.py
 pilkit/processors/utils.py
@@ -27,5 +36,7 @@
 tests/test_processors.py
 tests/test_utils.py
 tests/utils.py
+tests/assets/GaussianBlur_radius_3.png
+tests/assets/GaussianBlur_radius_7.png
 tests/assets/cat.gif
 tests/assets/reference.png
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit.egg-info/pbr.json 
new/pilkit-3.0/pilkit.egg-info/pbr.json
--- old/pilkit-2.0/pilkit.egg-info/pbr.json     1970-01-01 01:00:00.000000000 
+0100
+++ new/pilkit-3.0/pilkit.egg-info/pbr.json     2016-02-25 21:03:24.000000000 
+0100
@@ -0,0 +1 @@
+{"is_release": false, "git_version": "a636041"}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/pilkit.egg-info/requires.txt 
new/pilkit-3.0/pilkit.egg-info/requires.txt
--- old/pilkit-2.0/pilkit.egg-info/requires.txt 1970-01-01 01:00:00.000000000 
+0100
+++ new/pilkit-3.0/pilkit.egg-info/requires.txt 2023-09-28 00:01:05.000000000 
+0200
@@ -0,0 +1 @@
+Pillow>=7.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/setup.cfg new/pilkit-3.0/setup.cfg
--- old/pilkit-2.0/setup.cfg    2017-02-17 13:07:49.000000000 +0100
+++ new/pilkit-3.0/setup.cfg    2023-09-28 00:01:05.113009700 +0200
@@ -1,5 +1,4 @@
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/setup.py new/pilkit-3.0/setup.py
--- old/pilkit-2.0/setup.py     2017-02-17 12:45:57.000000000 +0100
+++ new/pilkit-3.0/setup.py     2023-09-27 02:34:34.000000000 +0200
@@ -1,15 +1,13 @@
-#/usr/bin/env python
+#!/usr/bin/env python
 import codecs
 import os
 from setuptools import setup, find_packages
 
-# Workaround for multiprocessing/nose issue. See 
http://bugs.python.org/msg170215
-try:
-    import multiprocessing
-except ImportError:
-    pass
 
-read = lambda filepath: codecs.open(filepath, 'r', 'utf-8').read()
+def read(filepath):
+    with codecs.open(filepath, 'r', 'utf-8') as f:
+        return f.read()
+
 
 # Load package meta from the pkgmeta module without loading the package.
 pkgmeta = {}
@@ -22,7 +20,7 @@
 setup(
     name='pilkit',
     version=pkgmeta['__version__'],
-    description='A collection of utilities and processors for the Python 
Imaging Libary.',
+    description='A collection of utilities and processors for the Python 
Imaging Library.',
     long_description=read(os.path.join(os.path.dirname(__file__), 
'README.rst')),
     author='Matthew Tretter',
     author_email='m...@tthewwithanm.com',
@@ -31,25 +29,20 @@
     packages=find_packages(exclude=['tests', 'tests.*']),
     zip_safe=False,
     include_package_data=True,
-    tests_require=[
-        'mock>=1.0.1',
-        'nose>=1.3.6',
-        'nose-progressive>=1.5.1',
-        'Pillow',
+    install_requires=[
+        'Pillow>=7.0'
     ],
-    test_suite='nose.collector',
-    install_requires=[],
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: BSD License',
         'Operating System :: OS Independent',
-        'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.3',
-        'Programming Language :: Python :: 3.4',
-        'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
+        'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
+        'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
         'Topic :: Utilities'
     ],
 )
Binary files old/pilkit-2.0/tests/assets/GaussianBlur_radius_3.png and 
new/pilkit-3.0/tests/assets/GaussianBlur_radius_3.png differ
Binary files old/pilkit-2.0/tests/assets/GaussianBlur_radius_7.png and 
new/pilkit-3.0/tests/assets/GaussianBlur_radius_7.png differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/tests/test_processors.py 
new/pilkit-3.0/tests/test_processors.py
--- old/pilkit-2.0/tests/test_processors.py     2017-02-17 12:45:57.000000000 
+0100
+++ new/pilkit-3.0/tests/test_processors.py     2023-09-27 02:27:32.000000000 
+0200
@@ -1,21 +1,23 @@
+import os
+from unittest import mock
+import pytest
+
 from pilkit.lib import Image, ImageDraw, ImageColor
 from pilkit.processors import (Resize, ResizeToFill, ResizeToFit, SmartCrop,
-                               SmartResize, MakeOpaque, ColorOverlay)
-from nose.tools import eq_, assert_true
-import os
+                               SmartResize, MakeOpaque, ColorOverlay, Convert,
+                               GaussianBlur)
 from pilkit.processors.resize import Thumbnail
-from .utils import create_image
-import mock
+from .utils import create_image, compare_images, get_image_file
 
 
 def test_smartcrop():
     img = SmartCrop(100, 100).process(create_image())
-    eq_(img.size, (100, 100))
+    assert img.size == (100, 100)
 
 
 def test_resizetofill():
     img = ResizeToFill(100, 100).process(create_image())
-    eq_(img.size, (100, 100))
+    assert img.size == (100, 100)
 
 
 def test_resizetofit():
@@ -26,7 +28,7 @@
     img = ResizeToFit(100, 100).process(img)
 
     # Assert that the image has maintained the aspect ratio.
-    eq_(img.size, (100, 50))
+    assert img.size == (100, 50)
 
 
 def test_resize_rounding():
@@ -36,13 +38,13 @@
 
     img = Image.new('RGB', (95, 95))
     img = ResizeToFill(28, 28).process(img)
-    eq_(img.size, (28, 28))
+    assert img.size == (28, 28)
 
 
 def test_resizetofit_mat():
     img = Image.new('RGB', (200, 100))
     img = ResizeToFit(100, 100, mat_color=0x000000).process(img)
-    eq_(img.size, (100, 100))
+    assert img.size == (100, 100)
 
 
 def test_coloroverlay():
@@ -52,7 +54,16 @@
     img = Image.new('RGB', (200, 100))
     color = ImageColor.getrgb('#cc0000')
     img = ColorOverlay(color, overlay_opacity=1.0).process(img)
-    eq_(img.getpixel((0,0)), (204, 0, 0))
+    assert img.getpixel((0,0)) == (204, 0, 0)
+
+def test_convert():
+    img = Image.new('RGBA', (200, 100))
+
+    img_RGBa = Convert("RGBa").process(img)
+    assert img_RGBa.mode == "RGBa"
+
+    img_RGBa_RGBA = Convert("RGBA").process(img)
+    assert img_RGBa_RGBA.mode == "RGBA"
 
 
 def test_resize_antialiasing():
@@ -82,7 +93,7 @@
     # Count the number of colors
     color_count = len(list(filter(None, img.histogram())))
 
-    assert_true(color_count > 2)
+    assert color_count > 2
 
 
 def test_upscale():
@@ -95,37 +106,33 @@
 
     for P in [Resize, ResizeToFit, ResizeToFill, SmartResize]:
         img2 = P(500, 500, upscale=True).process(img)
-        eq_(img2.size, (500, 500))
+        assert img2.size == (500, 500)
 
         img2 = P(500, 500, upscale=False).process(img)
-        eq_(img2.size, (100, 100))
+        assert img2.size == (100, 100)
 
 
 def test_should_raise_exception_if_anchor_is_passed_and_crop_is_set_to_false():
-    try:
+    with pytest.raises(Exception, match=r"You can't specify an anchor point if 
crop is False."):
         Thumbnail(height=200, width=200, upscale=False, crop=False, anchor='t')
-    except Exception as e:
-        eq_(str(e), "You can't specify an anchor point if crop is False.")
 
 
 def test_should_set_crop_to_true_if_anchor_is_passed_without_crop():
     thumb = Thumbnail(height=200, width=200, upscale=False, anchor='t')
-    assert_true(thumb.crop)
+    assert thumb.crop
 
 
 def test_should_raise_exception_when_crop_is_passed_without_height_and_width():
     img = Image.new('RGB', (100, 100))
-    try:
+    with pytest.raises(Exception, match=r"You must provide both a width and 
height when cropping."):
         Thumbnail(crop=True).process(img)
-    except Exception as e:
-        eq_(str(e), 'You must provide both a width and height when cropping.')
 
 
 @mock.patch('pilkit.processors.resize.SmartResize')
 def test_should_call_smartresize_when_crop_not_passed(my_mock):
     img = Image.new('RGB', (100, 100))
     Thumbnail(height=200, width=200, upscale=False).process(img)
-    assert_true(my_mock.called)
+    assert my_mock.called
 
 
 @mock.patch('pilkit.processors.resize.SmartResize')
@@ -146,13 +153,27 @@
 def test_should_call_resizetofill_when_crop_and_ancho_is_passed(my_mock):
     img = Image.new('RGB', (100, 100))
     Thumbnail(height=200, width=200, anchor='fake').process(img)
-    assert_true(my_mock.called)
+    assert my_mock.called
 
 @mock.patch('pilkit.processors.resize.ResizeToFit')
 def test_should_call_resizetofit_when_crop_is_not_passed(my_mock):
     img = Image.new('RGB', (100, 100))
     Thumbnail(height=200, width=200, crop=False).process(img)
-    assert_true(my_mock.called)
+    assert my_mock.called
+
+def test_GaussianBlur_radius_3():
+    img = GaussianBlur(radius = 3).process(create_image())
+    img = img.crop((112,112,144,144))
+
+    expected_img = Image.open(get_image_file("GaussianBlur_radius_3.png"))
+    assert compare_images(img, expected_img)
+
+def test_GaussianBlur_radius_7():
+    img = GaussianBlur(radius=7).process(create_image())
+    img = img.crop((112, 112, 144, 144))
+
+    expected_img = Image.open(get_image_file("GaussianBlur_radius_7.png"))
+    assert compare_images(img, expected_img)
 
 def test_make_gifs_opaque():
     dir = os.path.dirname(__file__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/tests/test_utils.py 
new/pilkit-3.0/tests/test_utils.py
--- old/pilkit-2.0/tests/test_utils.py  2017-02-17 12:45:57.000000000 +0100
+++ new/pilkit-3.0/tests/test_utils.py  2023-09-27 02:27:32.000000000 +0200
@@ -1,33 +1,35 @@
 import os
-from io import UnsupportedOperation
+import io
+import pytest
+from unittest.mock import Mock, patch
+from tempfile import NamedTemporaryFile
+
 from pilkit.exceptions import UnknownFormat, UnknownExtension
 from pilkit.lib import Image
 from pilkit.utils import (extension_to_format, format_to_extension, 
FileWrapper,
                           save_image, prepare_image, quiet)
-from mock import Mock, patch
-from nose.tools import eq_, raises, ok_
-from tempfile import NamedTemporaryFile
+
 from .utils import create_image
 
 
 def test_extension_to_format():
-    eq_(extension_to_format('.jpeg'), 'JPEG')
-    eq_(extension_to_format('.rgba'), 'SGI')
+    assert extension_to_format('.jpeg') == 'JPEG'
+    assert extension_to_format('.rgba') == 'SGI'
 
 
 def test_format_to_extension_no_init():
-    eq_(format_to_extension('PNG'), '.png')
-    eq_(format_to_extension('ICO'), '.ico')
+    assert format_to_extension('PNG') == '.png'
+    assert format_to_extension('ICO') == '.ico'
 
 
-@raises(UnknownFormat)
 def test_unknown_format():
-    format_to_extension('TXT')
+    with pytest.raises(UnknownFormat):
+        format_to_extension('TXT')
 
 
-@raises(UnknownExtension)
 def test_unknown_extension():
-    extension_to_format('.txt')
+    with pytest.raises(UnknownExtension):
+        extension_to_format('.txt')
 
 
 def test_default_extension():
@@ -40,17 +42,17 @@
     extensions we'd prefer, and this tests to make sure it's working.
 
     """
-    eq_(format_to_extension('JPEG'), '.jpg')
+    assert format_to_extension('JPEG') == '.jpg'
 
 
-@raises(AttributeError)
 def test_filewrapper():
 
     class K(object):
         def fileno(self):
-            raise UnsupportedOperation
+            raise io.UnsupportedOperation
 
-    FileWrapper(K()).fileno()
+    with pytest.raises(AttributeError):
+        FileWrapper(K()).fileno()
 
 
 def test_save_with_filename():
@@ -60,9 +62,8 @@
 
     """
     im = create_image()
-    outfile = NamedTemporaryFile()
-    save_image(im, outfile.name, 'JPEG')
-    outfile.close()
+    with NamedTemporaryFile() as outfile:
+        save_image(im, outfile.name, 'JPEG')
 
 
 def test_format_normalization():
@@ -71,7 +72,8 @@
     See https://github.com/matthewwithanm/django-imagekit/issues/262
     """
     im = Image.new('RGBA', (100, 100))
-    ok_('transparency' in prepare_image(im, 'gIF')[1])
+    assert 'transparency' in prepare_image(im, 'gIF')[1]
+
 
 def test_quiet():
     """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pilkit-2.0/tests/utils.py 
new/pilkit-3.0/tests/utils.py
--- old/pilkit-2.0/tests/utils.py       2017-02-17 12:45:57.000000000 +0100
+++ new/pilkit-3.0/tests/utils.py       2023-09-26 22:35:02.000000000 +0200
@@ -2,7 +2,7 @@
 from pilkit.lib import Image
 
 
-def get_image_file():
+def get_image_file(image_name='reference.png'):
     """
     See also:
 
@@ -11,9 +11,21 @@
 
     """
     dir = os.path.dirname(__file__)
-    path = os.path.join(dir, 'assets', 'reference.png')
+    path = os.path.join(dir, 'assets', image_name)
     return open(path, 'r+b')
 
-
 def create_image():
     return Image.open(get_image_file())
+
+def compare_images(a, b):
+  if a.size != b.size:
+    return False
+
+  rows, cols = a.size
+
+  for row in range(rows):
+    for col in range(cols):
+      if a.getpixel((row, col)) != b.getpixel((row, col)):
+        return False
+
+  return True

Reply via email to