From ef88be6548e6c53d8923fa749682c7d543b00630 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Thu, 23 Jul 2020 15:53:38 +0200 Subject: [PATCH 01/45] fix build on macos and clang when zip.h is present on system from zlib library --- src/decode.c | 2 +- src/encode.c | 2 +- src/libImaging/ZipDecode.c | 2 +- src/libImaging/ZipEncode.c | 2 +- src/libImaging/{Zip.h => ZipState.h} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename src/libImaging/{Zip.h => ZipState.h} (100%) diff --git a/src/decode.c b/src/decode.c index 25ce7316bc3..3d7eb5eabd3 100644 --- a/src/decode.c +++ b/src/decode.c @@ -807,7 +807,7 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) #ifdef HAVE_LIBZ -#include "Zip.h" +#include "ZipState.h" PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) diff --git a/src/encode.c b/src/encode.c index d64f47d2b13..e9a34418701 100644 --- a/src/encode.c +++ b/src/encode.c @@ -578,7 +578,7 @@ PyImaging_XbmEncoderNew(PyObject* self, PyObject* args) #ifdef HAVE_LIBZ -#include "Zip.h" +#include "ZipState.h" PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) diff --git a/src/libImaging/ZipDecode.c b/src/libImaging/ZipDecode.c index b0f8ad3261e..f9b12db7030 100644 --- a/src/libImaging/ZipDecode.c +++ b/src/libImaging/ZipDecode.c @@ -20,7 +20,7 @@ #ifdef HAVE_LIBZ -#include "Zip.h" +#include "ZipState.h" static const int OFFSET[] = { 7, 3, 3, 1, 1, 0, 0 }; static const int STARTING_COL[] = { 0, 4, 0, 2, 0, 1, 0 }; diff --git a/src/libImaging/ZipEncode.c b/src/libImaging/ZipEncode.c index 84ccb14ea54..67b7acbf824 100644 --- a/src/libImaging/ZipEncode.c +++ b/src/libImaging/ZipEncode.c @@ -19,7 +19,7 @@ #ifdef HAVE_LIBZ -#include "Zip.h" +#include "ZipState.h" int ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) diff --git a/src/libImaging/Zip.h b/src/libImaging/ZipState.h similarity index 100% rename from src/libImaging/Zip.h rename to src/libImaging/ZipState.h From 0e1a116c792a446b4cb75a8cd3da89abd6c121fb Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 2 Aug 2020 22:14:38 +0200 Subject: [PATCH 02/45] add develop instructions for winbuild --- winbuild/build.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/winbuild/build.rst b/winbuild/build.rst index aaed2c43fa8..ba568a0303d 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -81,6 +81,9 @@ Pillow for the selected version of Python. ``winbuild\build\build_pillow.cmd bdist_wheel`` will build wheels instead of installing Pillow. +You can also use ``winbuild\build\build_pillow.cmd --inplace develop`` to build +and install Pillow in develop mode (instead of ``pip install --editable``). + Testing Pillow -------------- From 8fad541531689f3ca7228de250b51e609596eacf Mon Sep 17 00:00:00 2001 From: navneeth Date: Thu, 6 Aug 2020 09:00:13 +0900 Subject: [PATCH 03/45] ENH: Autocontrast method enhancement - adding the option to specify mask for contrast computation --- Tests/test_imageops.py | 31 ++++++++++++++++++++++++++++++- src/PIL/ImageOps.py | 12 +++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 864df447eae..ef3589d156f 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,5 +1,5 @@ import pytest -from PIL import Image, ImageOps, features +from PIL import Image, ImageOps, ImageDraw, features from .helper import ( assert_image_equal, @@ -24,7 +24,9 @@ def test_sanity(): ImageOps.autocontrast(hopper("RGB")) ImageOps.autocontrast(hopper("L"), cutoff=10) + ImageOps.autocontrast(hopper("L"), cutoff=(2, 10)) ImageOps.autocontrast(hopper("L"), ignore=[0, 255]) + ImageOps.autocontrast(hopper("L"), mask=hopper("L")) ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255)) ImageOps.colorize(hopper("L"), "black", "white") @@ -311,3 +313,30 @@ def autocontrast(cutoff): assert autocontrast(10) == autocontrast((10, 10)) assert autocontrast(10) != autocontrast((1, 10)) + + +def test_autocontrast_mask(): + # Test the mask argument of autocontrast + with Image.open("Tests/images/bw_gradient.png") as img: + + rect_mask = Image.new("L", img.size, 0) + draw = ImageDraw.Draw(rect_mask) + x0, y0 = img.size[0]//4, img.size[1]//4 + x1, y1 = 3*img.size[0]//4, 3*img.size[1]//4 + draw.rectangle((x0, y0, x1, y1), fill=255) + + assert ImageOps.autocontrast(img, mask=rect_mask) != ImageOps.autocontrast(img) + + # Test the autocontrast with a rectangular mask + with Image.open("Tests/images/iptc.jpg") as img: + + rect_mask = Image.new("L", img.size, 0) + draw = ImageDraw.Draw(rect_mask) + x0, y0 = img.size[0]//2, img.size[1]//2 + x1, y1 = img.size[0]-40, img.size[1] + draw.rectangle((x0, y0, x1, y1), fill=255) + + result = ImageOps.autocontrast(img, mask=rect_mask) + result_nomask = ImageOps.autocontrast(img) + + assert result_nomask != result diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 157da0b521c..a6b7375a536 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -61,10 +61,10 @@ def _lut(image, lut): # actions -def autocontrast(image, cutoff=0, ignore=None): +def autocontrast(image, cutoff=0, ignore=None, mask=None): """ Maximize (normalize) image contrast. This function calculates a - histogram of the input image, removes **cutoff** percent of the + histogram of the input image (or mask region), removes **cutoff** percent of the lightest and darkest pixels from the histogram, and remaps the image so that the darkest pixel becomes black (0), and the lightest becomes white (255). @@ -74,9 +74,15 @@ def autocontrast(image, cutoff=0, ignore=None): high ends. Either a tuple of (low, high), or a single number for both. :param ignore: The background pixel value (use None for no background). + :param mask: histogram used in contrast operation is computed using pixels + within the mask. If no mask is given the entire image is used + for histogram computation. :return: An image. """ - histogram = image.histogram() + if mask: + histogram = image.histogram(mask) + else: + histogram = image.histogram() lut = [] for layer in range(0, len(histogram), 256): h = histogram[layer : layer + 256] From 5aae369c94bc4ffd6da8038bf9e771b2eb937a47 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 6 Aug 2020 23:14:49 +1000 Subject: [PATCH 04/45] Added homebrew zlib include directory --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index cc27684316b..ce5902621d1 100755 --- a/setup.py +++ b/setup.py @@ -479,6 +479,9 @@ def build_extensions(self): # add Homebrew's include and lib directories _add_directory(library_dirs, os.path.join(prefix, "lib")) _add_directory(include_dirs, os.path.join(prefix, "include")) + _add_directory( + include_dirs, os.path.join(prefix, "opt", "zlib", "include") + ) ft_prefix = os.path.join(prefix, "opt", "freetype") if ft_prefix and os.path.isdir(ft_prefix): From 665b4140333d359a1e621cb1a7448d3866ced146 Mon Sep 17 00:00:00 2001 From: Navneeth Subramanian Date: Fri, 7 Aug 2020 14:15:55 +0900 Subject: [PATCH 05/45] Update src/PIL/ImageOps.py Co-authored-by: Hugo van Kemenade --- src/PIL/ImageOps.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index a6b7375a536..6cb2ba96444 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -79,10 +79,7 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None): for histogram computation. :return: An image. """ - if mask: - histogram = image.histogram(mask) - else: - histogram = image.histogram() + histogram = image.histogram(mask) lut = [] for layer in range(0, len(histogram), 256): h = histogram[layer : layer + 256] From ca3796f4f60347c2aa37995445705a9d589c2d6d Mon Sep 17 00:00:00 2001 From: Navneeth Subramanian Date: Fri, 7 Aug 2020 14:16:07 +0900 Subject: [PATCH 06/45] Update src/PIL/ImageOps.py Co-authored-by: Hugo van Kemenade --- src/PIL/ImageOps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 6cb2ba96444..87d459fd1ca 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -74,7 +74,7 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None): high ends. Either a tuple of (low, high), or a single number for both. :param ignore: The background pixel value (use None for no background). - :param mask: histogram used in contrast operation is computed using pixels + :param mask: Histogram used in contrast operation is computed using pixels within the mask. If no mask is given the entire image is used for histogram computation. :return: An image. From fa493809a6cf088e70c66d9167f71a510c1f2dbc Mon Sep 17 00:00:00 2001 From: navneeth Date: Fri, 7 Aug 2020 14:31:17 +0900 Subject: [PATCH 07/45] ENH: Autocontrast - Code review feedback --- Tests/test_imageops.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index ef3589d156f..3d5223d98f1 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,5 +1,5 @@ import pytest -from PIL import Image, ImageOps, ImageDraw, features +from PIL import Image, ImageDraw, ImageOps, features from .helper import ( assert_image_equal, @@ -315,25 +315,27 @@ def autocontrast(cutoff): assert autocontrast(10) != autocontrast((1, 10)) -def test_autocontrast_mask(): +def test_autocontrast_mask_toy_input(): # Test the mask argument of autocontrast with Image.open("Tests/images/bw_gradient.png") as img: rect_mask = Image.new("L", img.size, 0) draw = ImageDraw.Draw(rect_mask) - x0, y0 = img.size[0]//4, img.size[1]//4 - x1, y1 = 3*img.size[0]//4, 3*img.size[1]//4 + x0, y0 = img.size[0] // 4, img.size[1] // 4 + x1, y1 = 3 * img.size[0] // 4, 3 * img.size[1] // 4 draw.rectangle((x0, y0, x1, y1), fill=255) assert ImageOps.autocontrast(img, mask=rect_mask) != ImageOps.autocontrast(img) + +def test_auto_contrast_mask_real_input(): # Test the autocontrast with a rectangular mask with Image.open("Tests/images/iptc.jpg") as img: rect_mask = Image.new("L", img.size, 0) draw = ImageDraw.Draw(rect_mask) - x0, y0 = img.size[0]//2, img.size[1]//2 - x1, y1 = img.size[0]-40, img.size[1] + x0, y0 = img.size[0] // 2, img.size[1] // 2 + x1, y1 = img.size[0] - 40, img.size[1] draw.rectangle((x0, y0, x1, y1), fill=255) result = ImageOps.autocontrast(img, mask=rect_mask) From faf913d0f864333b4c933dda7549a047aa5ad99b Mon Sep 17 00:00:00 2001 From: Navneeth Subramanian Date: Sat, 8 Aug 2020 19:32:52 +0900 Subject: [PATCH 08/45] Update Tests/test_imageops.py Co-authored-by: Hugo van Kemenade --- Tests/test_imageops.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 3d5223d98f1..90f874b6020 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -321,8 +321,10 @@ def test_autocontrast_mask_toy_input(): rect_mask = Image.new("L", img.size, 0) draw = ImageDraw.Draw(rect_mask) - x0, y0 = img.size[0] // 4, img.size[1] // 4 - x1, y1 = 3 * img.size[0] // 4, 3 * img.size[1] // 4 + x0 = img.size[0] // 4 + y0 = img.size[1] // 4 + x1 = 3 * img.size[0] // 4 + y1 = 3 * img.size[1] // 4 draw.rectangle((x0, y0, x1, y1), fill=255) assert ImageOps.autocontrast(img, mask=rect_mask) != ImageOps.autocontrast(img) From 24c1a32ed38d12a9bba3e4efa2fad5ee185ccad3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 9 Aug 2020 20:08:38 +0300 Subject: [PATCH 09/45] Add release notes for 8.0.0 and a template --- docs/conf.py | 2 +- docs/deprecations.rst | 34 ++++++++-------- docs/reference/ImageChops.rst | 7 +--- docs/releasenotes/8.0.0.rst | 73 ++++++++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + docs/releasenotes/template.rst | 45 +++++++++++++++++++++ 6 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 docs/releasenotes/8.0.0.rst create mode 100644 docs/releasenotes/template.rst diff --git a/docs/conf.py b/docs/conf.py index 78841cc15a4..a022e61cb07 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -77,7 +77,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ["_build"] +exclude_patterns = ["_build", "releasenotes/template.rst"] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 10ccec632eb..a619f133cb4 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -18,16 +18,16 @@ Image.show command parameter .. deprecated:: 7.2.0 The ``command`` parameter was deprecated and will be removed in a future release. -Use a subclass of ``ImageShow.Viewer`` instead. +Use a subclass of :py:class:`.ImageShow.Viewer` instead. Image._showxv ~~~~~~~~~~~~~ .. deprecated:: 7.2.0 -``Image._showxv`` has been deprecated. Use :py:meth:`~PIL.Image.Image.show` -instead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add -a custom :py:class:`~PIL.ImageShow.Viewer` class. +``Image._showxv`` has been deprecated. Use :py:meth:`.Image.Image.show` +instead. If custom behaviour is required, use :py:func:`.ImageShow.register` to add +a custom :py:class:`.ImageShow.Viewer` class. ImageFile.raise_ioerror ~~~~~~~~~~~~~~~~~~~~~~~ @@ -61,7 +61,7 @@ im.offset .. deprecated:: 1.1.2 .. versionremoved:: 8.0.0 -``im.offset()`` has been removed, call ``ImageChops.offset()`` instead. +``im.offset()`` has been removed, call :py:func:`.ImageChops.offset()` instead. It was documented as deprecated in PIL 1.1.2, raised a ``DeprecationWarning`` since 1.1.5, @@ -88,20 +88,20 @@ ImageCms.CmsProfile attributes .. deprecated:: 3.2.0 .. versionremoved:: 8.0.0 -Some attributes in ``ImageCms.CmsProfile`` have been removed. From 6.0.0, they issued a -``DeprecationWarning``: +Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed. From 6.0.0, +they issued a ``DeprecationWarning``: -======================== =============================== +======================== =============================================================== Removed Use instead -======================== =============================== -``color_space`` Padded ``xcolor_space`` -``pcs`` Padded ``connection_space`` -``product_copyright`` Unicode ``copyright`` -``product_desc`` Unicode ``profile_description`` -``product_description`` Unicode ``profile_description`` -``product_manufacturer`` Unicode ``manufacturer`` -``product_model`` Unicode ``model`` -======================== =============================== +======================== =============================================================== +``color_space`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.xcolor_space` +``pcs`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.connection_space` +``product_copyright`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.copyright` +``product_desc`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` +``product_description`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` +``product_manufacturer`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.manufacturer` +``product_model`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.model` +======================== =============================================================== Python 2.7 ~~~~~~~~~~ diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst index 772d9c98363..9519361a7e6 100644 --- a/docs/reference/ImageChops.rst +++ b/docs/reference/ImageChops.rst @@ -39,12 +39,7 @@ operations in this module). .. autofunction:: PIL.ImageChops.soft_light .. autofunction:: PIL.ImageChops.hard_light .. autofunction:: PIL.ImageChops.overlay -.. py:method:: PIL.ImageChops.offset(image, xoffset, yoffset=None) - - Returns a copy of the image where data has been offset by the given - distances. Data wraps around the edges. If **yoffset** is omitted, it - is assumed to be equal to **xoffset**. - +.. autofunction:: PIL.ImageChops.offset .. autofunction:: PIL.ImageChops.screen .. autofunction:: PIL.ImageChops.subtract .. autofunction:: PIL.ImageChops.subtract_modulo diff --git a/docs/releasenotes/8.0.0.rst b/docs/releasenotes/8.0.0.rst new file mode 100644 index 00000000000..14b4bab84a1 --- /dev/null +++ b/docs/releasenotes/8.0.0.rst @@ -0,0 +1,73 @@ +8.0.0 +----- + +Backwards Incompatible Changes +============================== + +Python 3.5 +^^^^^^^^^^ + +Pillow has dropped support for Python 3.5, which reached end-of-life on 2020-09-13. + +im.offset +^^^^^^^^^ + +``im.offset()`` has been removed, call :py:func:`.ImageChops.offset()` instead. + +Image.fromstring, im.fromstring and im.tostring +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* ``Image.fromstring()`` has been removed, call :py:func:`.Image.frombytes()` instead. +* ``im.fromstring()`` has been removed, call :py:meth:`~PIL.Image.Image.frombytes()` instead. +* ``im.tostring()`` has been removed, call :py:meth:`~PIL.Image.Image.tobytes()` instead. + +ImageCms.CmsProfile attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed: + +======================== =============================================================== +Removed Use instead +======================== =============================================================== +``color_space`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.xcolor_space` +``pcs`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.connection_space` +``product_copyright`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.copyright` +``product_desc`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` +``product_description`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` +``product_manufacturer`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.manufacturer` +``product_model`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.model` +======================== =============================================================== + +API Changes +=========== + +Add MIME type to PsdImagePlugin +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +"image/vnd.adobe.photoshop" is now registered as the +:py:class:`.PsdImagePlugin.PsdImageFile` MIME type. + +API Additions +============= + +ImageOps.autocontrast cutoffs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, the ``cutoff`` parameter of :py:func:`.ImageOps.autocontrast` could only +be a single integer, used as the percent to cut off from the histogram on the low and +high ends. + +Now, it can also be a tuple ``(low, high)``. + +Security +======== + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 2d7747c3e9b..ba81fbaf814 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -13,6 +13,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 8.0.0 7.2.0 7.1.2 7.1.1 diff --git a/docs/releasenotes/template.rst b/docs/releasenotes/template.rst new file mode 100644 index 00000000000..bf381114efa --- /dev/null +++ b/docs/releasenotes/template.rst @@ -0,0 +1,45 @@ +x.y.z +----- + +Backwards Incompatible Changes +============================== + +TODO +^^^^ + +Deprecations +============ + +TODO +^^^^ + +TODO + +API Changes +=========== + +TODO +^^^^ + +TODO + +API Additions +============= + +TODO +^^^^ + +TODO + +Security +======== + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO From 2c460c3ba13f54dca414952029a7ea57b9fe405a Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 9 Aug 2020 20:44:51 +0200 Subject: [PATCH 10/45] simplify ImageCms links in docs --- docs/deprecations.rst | 14 +++++++------- docs/releasenotes/8.0.0.rst | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index a619f133cb4..c60c17306c8 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -94,13 +94,13 @@ they issued a ``DeprecationWarning``: ======================== =============================================================== Removed Use instead ======================== =============================================================== -``color_space`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.xcolor_space` -``pcs`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.connection_space` -``product_copyright`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.copyright` -``product_desc`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` -``product_description`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` -``product_manufacturer`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.manufacturer` -``product_model`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.model` +``color_space`` Padded :py:attr:`~.CmsProfile.xcolor_space` +``pcs`` Padded :py:attr:`~.CmsProfile.connection_space` +``product_copyright`` Unicode :py:attr:`~.CmsProfile.copyright` +``product_desc`` Unicode :py:attr:`~.CmsProfile.profile_description` +``product_description`` Unicode :py:attr:`~.CmsProfile.profile_description` +``product_manufacturer`` Unicode :py:attr:`~.CmsProfile.manufacturer` +``product_model`` Unicode :py:attr:`~.CmsProfile.model` ======================== =============================================================== Python 2.7 diff --git a/docs/releasenotes/8.0.0.rst b/docs/releasenotes/8.0.0.rst index 14b4bab84a1..a09971b60a5 100644 --- a/docs/releasenotes/8.0.0.rst +++ b/docs/releasenotes/8.0.0.rst @@ -29,13 +29,13 @@ Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed: ======================== =============================================================== Removed Use instead ======================== =============================================================== -``color_space`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.xcolor_space` -``pcs`` Padded :py:attr:`~PIL.ImageCms.CmsProfile.connection_space` -``product_copyright`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.copyright` -``product_desc`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` -``product_description`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.profile_description` -``product_manufacturer`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.manufacturer` -``product_model`` Unicode :py:attr:`~PIL.ImageCms.CmsProfile.model` +``color_space`` Padded :py:attr:`~.CmsProfile.xcolor_space` +``pcs`` Padded :py:attr:`~.CmsProfile.connection_space` +``product_copyright`` Unicode :py:attr:`~.CmsProfile.copyright` +``product_desc`` Unicode :py:attr:`~.CmsProfile.profile_description` +``product_description`` Unicode :py:attr:`~.CmsProfile.profile_description` +``product_manufacturer`` Unicode :py:attr:`~.CmsProfile.manufacturer` +``product_model`` Unicode :py:attr:`~.CmsProfile.model` ======================== =============================================================== API Changes From bf4e918252d83938713d5eace9f362d4b6a6fe97 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 9 Aug 2020 20:48:44 +0100 Subject: [PATCH 11/45] adjust table marker lines to match the longest row Co-authored-by: Hugo van Kemenade --- docs/deprecations.rst | 7 ++++--- docs/releasenotes/8.0.0.rst | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index c60c17306c8..e3ad2a9e334 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -91,9 +91,10 @@ ImageCms.CmsProfile attributes Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed. From 6.0.0, they issued a ``DeprecationWarning``: -======================== =============================================================== +======================== =================================================== + Removed Use instead -======================== =============================================================== +======================== =================================================== ``color_space`` Padded :py:attr:`~.CmsProfile.xcolor_space` ``pcs`` Padded :py:attr:`~.CmsProfile.connection_space` ``product_copyright`` Unicode :py:attr:`~.CmsProfile.copyright` @@ -101,7 +102,7 @@ Removed Use instead ``product_description`` Unicode :py:attr:`~.CmsProfile.profile_description` ``product_manufacturer`` Unicode :py:attr:`~.CmsProfile.manufacturer` ``product_model`` Unicode :py:attr:`~.CmsProfile.model` -======================== =============================================================== +======================== =================================================== Python 2.7 ~~~~~~~~~~ diff --git a/docs/releasenotes/8.0.0.rst b/docs/releasenotes/8.0.0.rst index a09971b60a5..399e8b1c7bd 100644 --- a/docs/releasenotes/8.0.0.rst +++ b/docs/releasenotes/8.0.0.rst @@ -26,9 +26,9 @@ ImageCms.CmsProfile attributes Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed: -======================== =============================================================== +======================== =================================================== Removed Use instead -======================== =============================================================== +======================== =================================================== ``color_space`` Padded :py:attr:`~.CmsProfile.xcolor_space` ``pcs`` Padded :py:attr:`~.CmsProfile.connection_space` ``product_copyright`` Unicode :py:attr:`~.CmsProfile.copyright` @@ -36,7 +36,7 @@ Removed Use instead ``product_description`` Unicode :py:attr:`~.CmsProfile.profile_description` ``product_manufacturer`` Unicode :py:attr:`~.CmsProfile.manufacturer` ``product_model`` Unicode :py:attr:`~.CmsProfile.model` -======================== =============================================================== +======================== =================================================== API Changes =========== From ecc9cf3d732c2e8996b508db535472aa0449874f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 10 Aug 2020 14:14:12 +0300 Subject: [PATCH 12/45] "cutoff" can also be a float Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/releasenotes/8.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/8.0.0.rst b/docs/releasenotes/8.0.0.rst index 399e8b1c7bd..3fe044a9473 100644 --- a/docs/releasenotes/8.0.0.rst +++ b/docs/releasenotes/8.0.0.rst @@ -54,7 +54,7 @@ ImageOps.autocontrast cutoffs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Previously, the ``cutoff`` parameter of :py:func:`.ImageOps.autocontrast` could only -be a single integer, used as the percent to cut off from the histogram on the low and +be a single number, used as the percent to cut off from the histogram on the low and high ends. Now, it can also be a tuple ``(low, high)``. From 4841bbe2a53ff62ef76e6dbc35c517ad3d76a38d Mon Sep 17 00:00:00 2001 From: navneeth Date: Tue, 11 Aug 2020 23:36:39 +0900 Subject: [PATCH 13/45] added explicit test for autocontrast implementation - pixel value check --- Tests/test_imageops.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 6b69e8969bc..6618b5e5d9d 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,6 +1,6 @@ import pytest -from PIL import Image, ImageDraw, ImageOps, features +from PIL import Image, ImageDraw, ImageOps, ImageStat, features from .helper import ( assert_image_equal, @@ -328,8 +328,12 @@ def test_autocontrast_mask_toy_input(): y1 = 3 * img.size[1] // 4 draw.rectangle((x0, y0, x1, y1), fill=255) - assert ImageOps.autocontrast(img, mask=rect_mask) != ImageOps.autocontrast(img) + result = ImageOps.autocontrast(img, mask=rect_mask) + result_nomask = ImageOps.autocontrast(img) + assert result != result_nomask + assert ImageStat.Stat(result, mask=rect_mask).median == [127] + assert ImageStat.Stat(result_nomask).median == [128] def test_auto_contrast_mask_real_input(): # Test the autocontrast with a rectangular mask @@ -345,3 +349,5 @@ def test_auto_contrast_mask_real_input(): result_nomask = ImageOps.autocontrast(img) assert result_nomask != result + assert ImageStat.Stat(result, mask=rect_mask).median == [195, 202, 184] + assert ImageStat.Stat(result_nomask).median == [119, 106, 79] From 01aeaa4cada9469a9874dea34a5375e4d6cf8f97 Mon Sep 17 00:00:00 2001 From: navneeth Date: Tue, 11 Aug 2020 23:47:48 +0900 Subject: [PATCH 14/45] added explicit test for autocontrast implementation - pixel value check --- Tests/test_imageops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 6618b5e5d9d..69a7b9d030a 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -335,6 +335,7 @@ def test_autocontrast_mask_toy_input(): assert ImageStat.Stat(result, mask=rect_mask).median == [127] assert ImageStat.Stat(result_nomask).median == [128] + def test_auto_contrast_mask_real_input(): # Test the autocontrast with a rectangular mask with Image.open("Tests/images/iptc.jpg") as img: From d0de431fe47c11ec53f727dcdcae1323d531f4fb Mon Sep 17 00:00:00 2001 From: navneeth Date: Wed, 12 Aug 2020 00:06:16 +0900 Subject: [PATCH 15/45] testing with approx tuple to fix ubuntu test failures --- Tests/test_imageops.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 69a7b9d030a..d4328261d09 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -350,5 +350,13 @@ def test_auto_contrast_mask_real_input(): result_nomask = ImageOps.autocontrast(img) assert result_nomask != result - assert ImageStat.Stat(result, mask=rect_mask).median == [195, 202, 184] - assert ImageStat.Stat(result_nomask).median == [119, 106, 79] + assert_tuple_approx_equal( + ImageStat.Stat(result, mask=rect_mask).median, + [195, 202, 184], + threshold=2, + msg='autocontrast with mask pixel incorrect') + assert_tuple_approx_equal( + ImageStat.Stat(result_nomask).median, + [119, 106, 79], + threshold=2, + msg='autocontrast without mask pixel incorrect') From 2d3a841e4bd42559d8d3e9699c830326a4dea4dd Mon Sep 17 00:00:00 2001 From: navneeth Date: Wed, 12 Aug 2020 00:08:29 +0900 Subject: [PATCH 16/45] testing with approx tuple to fix ubuntu test failures --- Tests/test_imageops.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index d4328261d09..f17bfdd2f60 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -351,12 +351,14 @@ def test_auto_contrast_mask_real_input(): assert result_nomask != result assert_tuple_approx_equal( - ImageStat.Stat(result, mask=rect_mask).median, - [195, 202, 184], - threshold=2, - msg='autocontrast with mask pixel incorrect') + ImageStat.Stat(result, mask=rect_mask).median, + [195, 202, 184], + threshold=2, + msg="autocontrast with mask pixel incorrect", + ) assert_tuple_approx_equal( - ImageStat.Stat(result_nomask).median, - [119, 106, 79], - threshold=2, - msg='autocontrast without mask pixel incorrect') + ImageStat.Stat(result_nomask).median, + [119, 106, 79], + threshold=2, + msg="autocontrast without mask pixel incorrect", + ) From a2597a5683ce1705d6430c4359a308a8ba67e240 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 14 Aug 2020 21:12:01 +1000 Subject: [PATCH 17/45] Corrected inverted CMYK colors --- src/PIL/PdfImagePlugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index 47500baf7d0..b7668364bdc 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -121,6 +121,7 @@ def _save(im, fp, filename, save_all=False): bits = 8 params = None + decode = None if im.mode == "1": filter = "ASCIIHexDecode" @@ -150,6 +151,7 @@ def _save(im, fp, filename, save_all=False): filter = "DCTDecode" colorspace = PdfParser.PdfName("DeviceCMYK") procset = "ImageC" # color images + decode = [1, 0, 1, 0, 1, 0, 1, 0] else: raise ValueError("cannot save mode %s" % im.mode) @@ -189,6 +191,7 @@ def _save(im, fp, filename, save_all=False): Height=height, # * 72.0 / resolution, Filter=PdfParser.PdfName(filter), BitsPerComponent=bits, + Decode=decode, DecodeParams=params, ColorSpace=colorspace, ) From a302b3e3bcf26e943e0355c1d6a1d2bb6023e540 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Aug 2020 00:34:20 +1000 Subject: [PATCH 18/45] Pillow Wheels has changed to use GitHub releases [ci skip] --- RELEASING.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 3f62a70c484..99be3874980 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -101,11 +101,7 @@ Released as needed privately to individual vendors for critical security-related cd pillow-wheels ./update-pillow-tag.sh [[release tag]] ``` -* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/). - ```bash - wget -m -A 'Pillow--*' \ - http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com - ``` +* [ ] Download distributions from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases). ## Publicize Release From 48ba6b40f9bec71479c044d9b52da99eff24a35b Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 15 Aug 2020 00:42:43 +1000 Subject: [PATCH 19/45] Clarified wording [ci skip] Co-authored-by: Hugo van Kemenade --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 99be3874980..c9a0439d809 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -101,7 +101,7 @@ Released as needed privately to individual vendors for critical security-related cd pillow-wheels ./update-pillow-tag.sh [[release tag]] ``` -* [ ] Download distributions from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases). +* [ ] Download wheels from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases). ## Publicize Release From cb79b1fa8900adc1617e9518bb8a83d671dc9246 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Aug 2020 09:40:25 +1000 Subject: [PATCH 20/45] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c9389ddb1c9..0604a0411aa 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,9 @@ Changelog (Pillow) - Remove long-deprecated Image.py functions #4798 [hugovk, nulano, radarhere] +- Replaced most uses of distutils with setuptools #4797, #4809, #4814, #4817, #4829 + [hugovk, radarhere] + - Add MIME type to PsdImagePlugin #4788 [samamorgan] From c4b1657bd737def7f4aa069834ed4fbe1af33a55 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Aug 2020 09:50:39 +1000 Subject: [PATCH 21/45] Removed duplicate package --- .github/workflows/test-windows.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 0372b558629..79a5898aea3 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -226,7 +226,6 @@ jobs: mingw-w64-x86_64-python3-olefile \ mingw-w64-x86_64-python3-numpy \ mingw-w64-x86_64-python3-pyqt5 \ - mingw-w64-x86_64-python3-numpy \ mingw-w64-x86_64-freetype \ mingw-w64-x86_64-lcms2 \ mingw-w64-x86_64-libwebp \ From 14af7bb5ac022bd4b19fb7f716a59bf5bea0c1bb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 29 Jul 2020 08:13:56 +1000 Subject: [PATCH 22/45] Do not try to close fp if fp is empty --- Tests/test_image_load.py | 9 +++++++++ src/PIL/Image.py | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Tests/test_image_load.py b/Tests/test_image_load.py index 7dd3b294f27..f7fe99bb4c2 100644 --- a/Tests/test_image_load.py +++ b/Tests/test_image_load.py @@ -1,3 +1,4 @@ +import logging import os import pytest @@ -23,6 +24,14 @@ def test_close(): im.getpixel((0, 0)) +def test_close_after_load(caplog): + im = Image.open("Tests/images/hopper.gif") + im.load() + with caplog.at_level(logging.DEBUG): + im.close() + assert len(caplog.records) == 0 + + def test_contextmanager(): fn = None with Image.open("Tests/images/hopper.gif") as im: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8ab67d55e96..00adf2d0aa3 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -594,7 +594,8 @@ def close(self): try: if hasattr(self, "_close__fp"): self._close__fp() - self.fp.close() + if self.fp: + self.fp.close() self.fp = None except Exception as msg: logger.debug("Error closing: %s", msg) From ffb507519609b85b56d4c3b9f23e1fda9dd7423e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Aug 2020 19:42:23 +1000 Subject: [PATCH 23/45] Alphabetised dependencies --- .github/workflows/test-windows.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 79a5898aea3..8508a474804 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -218,22 +218,22 @@ jobs: - name: Install Dependencies run: | pacman -S --noconfirm \ - mingw-w64-x86_64-python3-pip \ - mingw-w64-x86_64-python3-setuptools \ - mingw-w64-x86_64-python3-pytest \ - mingw-w64-x86_64-python3-pytest-cov \ mingw-w64-x86_64-python3-cffi \ - mingw-w64-x86_64-python3-olefile \ mingw-w64-x86_64-python3-numpy \ + mingw-w64-x86_64-python3-olefile \ + mingw-w64-x86_64-python3-pip \ mingw-w64-x86_64-python3-pyqt5 \ + mingw-w64-x86_64-python3-pytest \ + mingw-w64-x86_64-python3-pytest-cov \ + mingw-w64-x86_64-python3-setuptools \ mingw-w64-x86_64-freetype \ + mingw-w64-x86_64-ghostscript \ mingw-w64-x86_64-lcms2 \ - mingw-w64-x86_64-libwebp \ - mingw-w64-x86_64-libjpeg-turbo \ - mingw-w64-x86_64-openjpeg2 \ mingw-w64-x86_64-libimagequant \ + mingw-w64-x86_64-libjpeg-turbo \ mingw-w64-x86_64-libraqm \ - mingw-w64-x86_64-ghostscript \ + mingw-w64-x86_64-libwebp \ + mingw-w64-x86_64-openjpeg2 \ subversion python3 -m pip install pyroma From 7f711ce91a3f6d6cdab61481af890e7e49f65a1a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 17 Aug 2020 09:45:10 +0300 Subject: [PATCH 24/45] Convert README to Markdown and add logo [CI skip] --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ README.rst | 103 ----------------------------------------------------- setup.py | 12 +++---- 3 files changed, 103 insertions(+), 109 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 00000000000..e5d6443c2a1 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +

+ Pillow logo +

+ +# Pillow + +## Python Imaging Library (Fork) + +Pillow is the friendly PIL fork by [Alex Clark and +Contributors](https://github.com/python-pillow/Pillow/graphs/contributors). +PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +As of 2019, Pillow development is +[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise>). + + + + + + + + + + + + + + + + + + +
docs + Documentation Status +
tests + Travis CI build status (Linux) + Travis CI build status (macOS) + AppVeyor CI build status (Windows) + GitHub Actions build status (Lint) + GitHub Actions build status (Test Linux and macOS) + GitHub Actions build status (Test Windows) + GitHub Actions build status (Test Docker) + Code coverage +
package + Zenodo + Tidelift + Newest PyPI version + Number of PyPI downloads +
social + Join the chat at https://gitter.im/python-pillow/Pillow + Follow on https://twitter.com/PythonPillow +
+ +## More Information + +- [Documentation](https://pillow.readthedocs.io/) + - [Installation](https://pillow.readthedocs.io/en/latest/installation.html) + - [Handbook](https://pillow.readthedocs.io/en/latest/handbook/index.html) +- [Contribute](https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md) + - [Issues](https://github.com/python-pillow/Pillow/issues) + - [Pull requests](https://github.com/python-pillow/Pillow/pulls) +- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst) + - [Pre-fork](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork) + +## Report a Vulnerability + +To report a security vulnerability, please follow the procedure described in the [Tidelift security policy](https://tidelift.com/docs/security). + diff --git a/README.rst b/README.rst deleted file mode 100644 index c1d5be57912..00000000000 --- a/README.rst +++ /dev/null @@ -1,103 +0,0 @@ -Pillow -====== - -Python Imaging Library (Fork) ------------------------------ - -Pillow is the friendly PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. As of 2019, Pillow development is `supported by Tidelift `_. - -.. start-badges - -.. list-table:: - :stub-columns: 1 - - * - docs - - |docs| - * - tests - - |linux| |macos| |windows| |gha_lint| |gha| |gha_windows| |gha_docker| |coverage| - * - package - - |zenodo| |tidelift| |version| |downloads| - * - social - - |gitter| |twitter| - -.. end-badges - -More Information ----------------- - -- `Documentation `_ - - - `Installation `_ - - `Handbook `_ - -- `Contribute `_ - - - `Issues `_ - - `Pull requests `_ - -- `Changelog `_ - - - `Pre-fork `_ - -Report a Vulnerability ----------------------- - -To report a security vulnerability, please follow the procedure described in the `Tidelift security policy `_. - -.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest - :target: https://pillow.readthedocs.io/?badge=latest - :alt: Documentation Status - -.. |linux| image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build - :target: https://travis-ci.org/python-pillow/Pillow - :alt: Travis CI build status (Linux) - -.. |macos| image:: https://img.shields.io/travis/python-pillow/pillow-wheels/master.svg?label=macOS%20build - :target: https://travis-ci.org/python-pillow/pillow-wheels - :alt: Travis CI build status (macOS) - -.. |windows| image:: https://img.shields.io/appveyor/build/python-pillow/Pillow/master.svg?label=Windows%20build - :target: https://ci.appveyor.com/project/python-pillow/Pillow - :alt: AppVeyor CI build status (Windows) - -.. |gha_lint| image:: https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg - :target: https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint - :alt: GitHub Actions build status (Lint) - -.. |gha_docker| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg - :target: https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22 - :alt: GitHub Actions build status (Test Docker) - -.. |gha| image:: https://github.com/python-pillow/Pillow/workflows/Test/badge.svg - :target: https://github.com/python-pillow/Pillow/actions?query=workflow%3ATest - :alt: GitHub Actions build status (Test Linux and macOS) - -.. |gha_windows| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Windows/badge.svg - :target: https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Windows%22 - :alt: GitHub Actions build status (Test Windows) - -.. |coverage| image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg - :target: https://codecov.io/gh/python-pillow/Pillow - :alt: Code coverage - -.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg - :target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow - -.. |tidelift| image:: https://tidelift.com/badges/package/pypi/Pillow?style=flat - :target: https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=badge - -.. |version| image:: https://img.shields.io/pypi/v/pillow.svg - :target: https://pypi.org/project/Pillow/ - :alt: Latest PyPI version - -.. |downloads| image:: https://img.shields.io/pypi/dm/pillow.svg - :target: https://pypi.org/project/Pillow/ - :alt: Number of PyPI downloads - -.. |gitter| image:: https://badges.gitter.im/python-pillow/Pillow.svg - :target: https://gitter.im/python-pillow/Pillow?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge - :alt: Join the chat at https://gitter.im/python-pillow/Pillow - -.. |twitter| image:: https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg - :target: https://twitter.com/PythonPillow - :alt: Follow on https://twitter.com/PythonPillow diff --git a/setup.py b/setup.py index cc27684316b..c85667f605f 100755 --- a/setup.py +++ b/setup.py @@ -243,11 +243,6 @@ def _cmd_exists(cmd): ) -def _read(file): - with open(file, "rb") as fp: - return fp.read() - - def _pkg_config(name): try: command = os.environ.get("PKG_CONFIG", "pkg-config") @@ -858,12 +853,17 @@ def debug_build(): Extension("PIL._imagingmath", ["src/_imagingmath.c"]), Extension("PIL._imagingmorph", ["src/_imagingmorph.c"]), ] + +with open("README.md") as f: + long_description = f.read() + try: setup( name=NAME, version=PILLOW_VERSION, description="Python Imaging Library (Fork)", - long_description=_read("README.rst").decode("utf-8"), + long_description=long_description, + long_description_content_type="text/markdown", license="HPND", author="Alex Clark (PIL Fork Author)", author_email="aclark@python-pillow.org", From 2fecde95e168c15320b01c7f939ee3371924d450 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 17 Aug 2020 14:41:32 +0300 Subject: [PATCH 25/45] Fix link Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e5d6443c2a1..6ca6cbf83d1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Pillow is the friendly PIL fork by [Alex Clark and Contributors](https://github.com/python-pillow/Pillow/graphs/contributors). PIL is the Python Imaging Library by Fredrik Lundh and Contributors. As of 2019, Pillow development is -[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise>). +[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise). @@ -94,4 +94,3 @@ As of 2019, Pillow development is ## Report a Vulnerability To report a security vulnerability, please follow the procedure described in the [Tidelift security policy](https://tidelift.com/docs/security). - From ff635923b2aa03559ea278e6dbf7a2628ecf7eca Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 19 Aug 2020 16:33:23 +0300 Subject: [PATCH 26/45] Update release notes: Add mask parameter to autocontrast --- docs/releasenotes/8.0.0.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/releasenotes/8.0.0.rst b/docs/releasenotes/8.0.0.rst index 3fe044a9473..bbb954e25b0 100644 --- a/docs/releasenotes/8.0.0.rst +++ b/docs/releasenotes/8.0.0.rst @@ -50,6 +50,14 @@ Add MIME type to PsdImagePlugin API Additions ============= +ImageOps.autocontrast: add mask parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:py:func:`.ImageOps.autocontrast` can now take a ``mask`` parameter: + +* Histogram used in contrast operation is computed using pixels within the mask. + If no mask is given the entire image is used for histogram computation. + ImageOps.autocontrast cutoffs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 3e6dae8455e127fde974f8e90db065a4bcdf5475 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 21 Aug 2020 22:30:25 +1000 Subject: [PATCH 27/45] Removed TravisCI setuptools version requirement --- .ci/install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 3c75526fe03..2143c06137a 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -32,7 +32,6 @@ pip install test-image-results pip install numpy # TODO Remove when 3.9-dev includes setuptools 49.3.2+: -if [ "$TRAVIS_PYTHON_VERSION" == "3.9-dev" ]; then pip install -U "setuptools>=49.3.2" ; fi if [ "$GHA_PYTHON_VERSION" == "3.9-dev" ]; then pip install -U "setuptools>=49.3.2" ; fi if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then From 45b228e835c35b9ae19877402b5041410ed4dee3 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Mon, 3 Aug 2020 18:07:38 +0200 Subject: [PATCH 28/45] Fix exception handling when saving images The e variable is already used in the for loop, use exc to store the exception. --- src/PIL/ImageFile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 81ec0c26659..b06b89811eb 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -509,7 +509,7 @@ def _save(im, fp, tile, bufsize=0): try: fh = fp.fileno() fp.flush() - except (AttributeError, io.UnsupportedOperation) as e: + except (AttributeError, io.UnsupportedOperation) as exc: # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) @@ -526,7 +526,7 @@ def _save(im, fp, tile, bufsize=0): if s: break if s < 0: - raise OSError("encoder error %d when writing image file" % s) from e + raise OSError("encoder error %d when writing image file" % s) from exc e.cleanup() else: # slight speedup: compress to real file object From 0af193afc0a66545667b651444256234f63ad1fe Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 4 Aug 2020 21:58:03 +1000 Subject: [PATCH 29/45] Added test --- Tests/test_imagefile.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index e0dbd909a3d..b4107e8e3a6 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -244,3 +244,8 @@ def test_no_format(self): im = MockImageFile(buf) assert im.format is None assert im.get_format_mimetype() is None + + def test_oserror(self): + im = Image.new("RGB", (1, 1)) + with pytest.raises(OSError): + im.save(BytesIO(), "JPEG2000") From 78e971913ce9db7c5a4749fc1a22f901b44c5e9e Mon Sep 17 00:00:00 2001 From: luphord Date: Fri, 24 Jul 2020 16:17:15 +0200 Subject: [PATCH 30/45] fix IFDRational equality --- src/PIL/TiffImagePlugin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 018789abf47..ea88ef0f723 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -353,7 +353,13 @@ def __hash__(self): return self._val.__hash__() def __eq__(self, other): - return self._val == other + if isinstance(other, IFDRational): + if self.denominator == 0 and other.denominator == 0: + # in this case self._val and other._val would be NaN + return self.numerator == other.numerator + return self._val == other._val + else: + return self._val == other def _delegate(op): def delegate(self, *args): From 9db5266fa724b58d6f5d4f200da5f0d32db78de3 Mon Sep 17 00:00:00 2001 From: luphord Date: Fri, 24 Jul 2020 16:17:49 +0200 Subject: [PATCH 31/45] test IFDRational equality --- Tests/test_tiff_ifdrational.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index 707284d7b4f..e976652782c 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -29,6 +29,13 @@ def test_sanity(): _test_equal(1, 2, IFDRational(1, 2)) +def test_ranges(): + + for num in range(1, 10): + for denom in range(1, 10): + assert IFDRational(num, denom) == IFDRational(num, denom) + + def test_nonetype(): # Fails if the _delegate function doesn't return a valid function @@ -43,6 +50,12 @@ def test_nonetype(): assert xres and yres +def test_nan(): + # usually NaN != NaN, but this would break exif self-equality + + assert IFDRational(123, 0) == IFDRational(123, 0) + + def test_ifd_rational_save(tmp_path): methods = (True, False) if not features.check("libtiff"): From 1fd9ccfe3c38fdca9abcb90fed91bc5dab8a4b05 Mon Sep 17 00:00:00 2001 From: luphord Date: Fri, 24 Jul 2020 16:30:47 +0200 Subject: [PATCH 32/45] test Exif self-equality --- Tests/test_file_jpeg.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 03a2dba51a1..ffb2d172518 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -241,6 +241,15 @@ def test_exif_gps(self): # Assert assert exif[gps_index] == expected_exif_gps + + def test_exif_equality(self): + # in 7.2.0 Exif rationals are read as TiffImagePlugin.IFDRational + # which broke self-equality of Exif data + exifs = [] + for i in range(2): + with Image.open("Tests/images/exif-200dpcm.jpg") as im: + exifs.append(im._getexif()) + assert exifs[0] == exifs[1] def test_exif_rollback(self): # rolling back exif support in 3.1 to pre-3.0 formatting. From ae5e0218e945b73c65b69b57cc9a61e08487ab97 Mon Sep 17 00:00:00 2001 From: luphord Date: Fri, 24 Jul 2020 17:05:28 +0200 Subject: [PATCH 33/45] remove whitespace --- Tests/test_file_jpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index ffb2d172518..ce8eee48a6e 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -241,7 +241,7 @@ def test_exif_gps(self): # Assert assert exif[gps_index] == expected_exif_gps - + def test_exif_equality(self): # in 7.2.0 Exif rationals are read as TiffImagePlugin.IFDRational # which broke self-equality of Exif data From 4bb35c57ddb6dcb68e3a355512432a30fbf748bc Mon Sep 17 00:00:00 2001 From: luphord <46622741+luphord@users.noreply.github.com> Date: Sat, 25 Jul 2020 07:09:00 +0200 Subject: [PATCH 34/45] clarify comment Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_file_jpeg.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index ce8eee48a6e..9fbff23e607 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -243,8 +243,9 @@ def test_exif_gps(self): assert exif[gps_index] == expected_exif_gps def test_exif_equality(self): - # in 7.2.0 Exif rationals are read as TiffImagePlugin.IFDRational - # which broke self-equality of Exif data + # In 7.2.0, Exif rationals were changed to be read as + # TiffImagePlugin.IFDRational. This class had a bug in __eq__, + # breaking the self-equality of Exif data exifs = [] for i in range(2): with Image.open("Tests/images/exif-200dpcm.jpg") as im: From 0a46cbfea9f1e01cf5b51f89311d64636da4eddd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Aug 2020 20:55:47 +1000 Subject: [PATCH 35/45] Reverted NaN change, so that NaN != NaN --- Tests/test_tiff_ifdrational.py | 7 ------- src/PIL/TiffImagePlugin.py | 8 ++------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index e976652782c..1697a8d4946 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -30,7 +30,6 @@ def test_sanity(): def test_ranges(): - for num in range(1, 10): for denom in range(1, 10): assert IFDRational(num, denom) == IFDRational(num, denom) @@ -50,12 +49,6 @@ def test_nonetype(): assert xres and yres -def test_nan(): - # usually NaN != NaN, but this would break exif self-equality - - assert IFDRational(123, 0) == IFDRational(123, 0) - - def test_ifd_rational_save(tmp_path): methods = (True, False) if not features.check("libtiff"): diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index ea88ef0f723..cb925246d47 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -354,12 +354,8 @@ def __hash__(self): def __eq__(self, other): if isinstance(other, IFDRational): - if self.denominator == 0 and other.denominator == 0: - # in this case self._val and other._val would be NaN - return self.numerator == other.numerator - return self._val == other._val - else: - return self._val == other + other = other._val + return self._val == other def _delegate(op): def delegate(self, *args): From c9ecfa85c15fe81a8edcebb0a18ae994a5f0d1a3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 29 Aug 2020 14:05:49 +1000 Subject: [PATCH 36/45] Updated harfbuzz to 2.7.2 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 653d6c0f623..175595ff5da 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -251,9 +251,9 @@ def cmd_msbuild( "libs": [r"*.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/2.7.1.zip", - "filename": "harfbuzz-2.7.1.zip", - "dir": "harfbuzz-2.7.1", + "url": "https://github.com/harfbuzz/harfbuzz/archive/2.7.2.zip", + "filename": "harfbuzz-2.7.2.zip", + "dir": "harfbuzz-2.7.2", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 3c9897799b74b55f91b4c3cf6150d3803d83ce50 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 30 Aug 2020 06:07:30 +0200 Subject: [PATCH 37/45] Revert "Merge pull request #4832 from radarhere/msys2_32" This reverts commit d0dd3444a2b4fe71bd134fbfc1f35af125d2e317, reversing changes made to afbbdf5f72faabe8d0eb92be7055a229b5b15101. --- .github/workflows/test-windows.yml | 48 ++++++++++++++++++------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 8508a474804..7f7343cd9a1 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -198,15 +198,25 @@ jobs: msys: runs-on: windows-2019 + strategy: + fail-fast: false + matrix: + mingw: ["MINGW32", "MINGW64"] + include: + - mingw: "MINGW32" + package: "mingw-w64-i686" + - mingw: "MINGW64" + package: "mingw-w64-x86_64" + defaults: run: shell: bash.exe --login -eo pipefail "{0}" env: - MSYSTEM: MINGW64 + MSYSTEM: ${{ matrix.mingw }} CHERE_INVOKING: 1 timeout-minutes: 30 - name: MSYS2 MinGW 64-bit + name: MSYS2 ${{ matrix.mingw }} steps: - uses: actions/checkout@v2 @@ -218,22 +228,22 @@ jobs: - name: Install Dependencies run: | pacman -S --noconfirm \ - mingw-w64-x86_64-python3-cffi \ - mingw-w64-x86_64-python3-numpy \ - mingw-w64-x86_64-python3-olefile \ - mingw-w64-x86_64-python3-pip \ - mingw-w64-x86_64-python3-pyqt5 \ - mingw-w64-x86_64-python3-pytest \ - mingw-w64-x86_64-python3-pytest-cov \ - mingw-w64-x86_64-python3-setuptools \ - mingw-w64-x86_64-freetype \ - mingw-w64-x86_64-ghostscript \ - mingw-w64-x86_64-lcms2 \ - mingw-w64-x86_64-libimagequant \ - mingw-w64-x86_64-libjpeg-turbo \ - mingw-w64-x86_64-libraqm \ - mingw-w64-x86_64-libwebp \ - mingw-w64-x86_64-openjpeg2 \ + ${{ matrix.package }}-python3-cffi \ + ${{ matrix.package }}-python3-numpy \ + ${{ matrix.package }}-python3-olefile \ + ${{ matrix.package }}-python3-pip \ + ${{ matrix.package }}-python3-pyqt5 \ + ${{ matrix.package }}-python3-pytest \ + ${{ matrix.package }}-python3-pytest-cov \ + ${{ matrix.package }}-python3-setuptools \ + ${{ matrix.package }}-freetype \ + ${{ matrix.package }}-ghostscript \ + ${{ matrix.package }}-lcms2 \ + ${{ matrix.package }}-libimagequant \ + ${{ matrix.package }}-libjpeg-turbo \ + ${{ matrix.package }}-libraqm \ + ${{ matrix.package }}-libwebp \ + ${{ matrix.package }}-openjpeg2 \ subversion python3 -m pip install pyroma @@ -255,4 +265,4 @@ jobs: python3 -m pip install codecov bash <(curl -s https://codecov.io/bash) -F GHA_Windows env: - CODECOV_NAME: MSYS2 MinGW 64-bit + CODECOV_NAME: MSYS2 ${{ matrix.mingw }} From facf73f8d0ffa08623e6ce542978f76cbc3699f0 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 30 Aug 2020 06:09:42 +0200 Subject: [PATCH 38/45] use proper names for MSYS2 --- .github/workflows/test-windows.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7f7343cd9a1..a90a0a95487 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -204,8 +204,10 @@ jobs: mingw: ["MINGW32", "MINGW64"] include: - mingw: "MINGW32" + name: "MSYS2 MinGW 32-bit" package: "mingw-w64-i686" - mingw: "MINGW64" + name: "MSYS2 MinGW 64-bit" package: "mingw-w64-x86_64" defaults: @@ -216,7 +218,7 @@ jobs: CHERE_INVOKING: 1 timeout-minutes: 30 - name: MSYS2 ${{ matrix.mingw }} + name: ${{ matrix.name }} steps: - uses: actions/checkout@v2 @@ -265,4 +267,4 @@ jobs: python3 -m pip install codecov bash <(curl -s https://codecov.io/bash) -F GHA_Windows env: - CODECOV_NAME: MSYS2 ${{ matrix.mingw }} + CODECOV_NAME: ${{ matrix.name }} From 2d29c5085ddf84028810838af4391cf81dbcb6ae Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 30 Aug 2020 14:32:28 +1000 Subject: [PATCH 39/45] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0604a0411aa..263c2a82367 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,24 @@ Changelog (Pillow) 8.0.0 (unreleased) ------------------ +- Fix IFDRational __eq__ bug #4888 + [luphord, radarhere] + +- Fixed duplicate variable name #4885 + [liZe, radarhere] + +- Added homebrew zlib include directory #4842 + [radarhere] + +- Corrected inverted PDF CMYK colors #4866 + [radarhere] + +- Do not try to close file pointer if file pointer is empty #4823 + [radarhere] + +- ImageOps.autocontrast: add mask parameter #4843 + [navneeth, hugovk] + - Read EXIF data tEXt chunk into info as bytes instead of string #4828 [radarhere] From 22b8b25f2f9de99a88e1ed71cad333c92b67abe9 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 31 Aug 2020 00:50:45 +0200 Subject: [PATCH 40/45] update CI targets --- docs/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 706cfb1d707..4c41cc9ee6f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -412,12 +412,12 @@ These platforms are built and tested for every change. | Windows Server 2016 | 3.8 |x86 | | +--------------------------+-----------------------+ | | 3.6 |x86-64 | -| +--------------------------+-----------------------+ -| | 3.7/MinGW |x86 | +----------------------------------+--------------------------+-----------------------+ | Windows Server 2019 | 3.6, 3.7, 3.8 |x86, x86-64 | | +--------------------------+-----------------------+ | | PyPy3 |x86 | +| +--------------------------+-----------------------+ +| | 3.8/MinGW |x86, x86-64 | +----------------------------------+--------------------------+-----------------------+ From 298b7d0333387a2bd0ee68c377723be487c8a051 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 31 Aug 2020 07:37:17 +1000 Subject: [PATCH 41/45] Update pre-commit --- .pre-commit-config.yaml | 6 +- Tests/check_jpeg_leaks.py | 156 +++++++++++++++---------------- Tests/check_libtiff_segfault.py | 6 +- Tests/test_bmp_reference.py | 12 +-- Tests/test_file_apng.py | 5 +- Tests/test_file_jpeg.py | 5 +- Tests/test_file_libtiff.py | 17 ++-- Tests/test_file_libtiff_small.py | 12 +-- Tests/test_file_tiff.py | 4 +- Tests/test_file_tiff_metadata.py | 8 +- Tests/test_format_hsv.py | 25 ++++- Tests/test_image.py | 5 +- Tests/test_image_access.py | 8 +- Tests/test_image_point.py | 6 +- Tests/test_imagecms.py | 2 +- Tests/test_imagedraw.py | 5 +- Tests/test_imagefont_bitmap.py | 5 +- Tests/test_imageshow.py | 6 +- src/PIL/Image.py | 6 +- src/PIL/ImageCms.py | 4 +- src/PIL/ImageMorph.py | 42 ++++----- src/PIL/PngImagePlugin.py | 9 +- src/PIL/TiffImagePlugin.py | 10 +- 23 files changed, 201 insertions(+), 163 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d619523d582..c8158c375bd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 6bedb5c58a7d8c25aa9509f8217bc24e9797e90d # frozen: 19.10b0 + rev: e66be67b9b6811913470f70c28b4d50f94d05b22 # frozen: 20.8b1 hooks: - id: black args: ["--target-version", "py35"] @@ -9,7 +9,7 @@ repos: types: [] - repo: https://github.com/timothycrosley/isort - rev: 9ae09866e278fbc6ec0383ccb16b5c84e78e6e4d # frozen: 5.3.2 + rev: 377d260ffa6f746693f97b46d95025afc4bd8275 # frozen: 5.4.2 hooks: - id: isort @@ -31,7 +31,7 @@ repos: additional_dependencies: [flake8-2020, flake8-implicit-str-concat] - repo: https://github.com/pre-commit/pygrep-hooks - rev: 20b9ac745c5adaab12b845b3564c773dcc051d0e # frozen: v1.5.2 + rev: eae6397e4c259ed3d057511f6dd5330b92867e62 # frozen: v1.6.0 hooks: - id: python-check-blanket-noqa - id: rst-backticks diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index b63fa2a1e59..ab8d7771992 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -119,60 +119,59 @@ def test_qtables_leak(): def test_exif_leak(): """ -pre patch: - - MB -177.1^ # - | @@@# - | :@@@@@@# - | ::::@@@@@@# - | ::::::::@@@@@@# - | @@::::: ::::@@@@@@# - | @@@@ ::::: ::::@@@@@@# - | @@@@@@@ ::::: ::::@@@@@@# - | @@::@@@@@@@ ::::: ::::@@@@@@# - | @@@@ : @@@@@@@ ::::: ::::@@@@@@# - | @@@@@@ @@ : @@@@@@@ ::::: ::::@@@@@@# - | @@@@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | @::@@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | ::::@: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | :@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | ::@@::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | @@::: @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | @::@ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | :::@: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - | @@@:: @: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# - 0 +----------------------------------------------------------------------->Gi - 0 11.37 - - -post patch: - - MB -21.06^ ::::::::::::::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | ##::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @@@@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: - 0 +----------------------------------------------------------------------->Gi - 0 11.33 - -""" + pre patch: + + MB + 177.1^ # + | @@@# + | :@@@@@@# + | ::::@@@@@@# + | ::::::::@@@@@@# + | @@::::: ::::@@@@@@# + | @@@@ ::::: ::::@@@@@@# + | @@@@@@@ ::::: ::::@@@@@@# + | @@::@@@@@@@ ::::: ::::@@@@@@# + | @@@@ : @@@@@@@ ::::: ::::@@@@@@# + | @@@@@@ @@ : @@@@@@@ ::::: ::::@@@@@@# + | @@@@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | @::@@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | ::::@: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | :@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | ::@@::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | @@::: @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | @::@ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | :::@: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + | @@@:: @: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@# + 0 +----------------------------------------------------------------------->Gi + 0 11.37 + + + post patch: + + MB + 21.06^ ::::::::::::::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | ##::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @@@@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + | @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@:::::: + 0 +----------------------------------------------------------------------->Gi + 0 11.33 + """ im = hopper("RGB") exif = b"12345678" * 4096 @@ -183,31 +182,30 @@ def test_exif_leak(): def test_base_save(): """ -base case: - MB -20.99^ ::::: :::::::::::::::::::::::::::::::::::::::::::@::: - | ##: : ::::::@::::::: :::: :::: : : : : : : :::::::::::: :::@::: - | # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @@# : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @@@ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | :@@@@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: - 0 +----------------------------------------------------------------------->Gi - 0 7.882 -""" + base case: + MB + 20.99^ ::::: :::::::::::::::::::::::::::::::::::::::::::@::: + | ##: : ::::::@::::::: :::: :::: : : : : : : :::::::::::: :::@::: + | # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @@# : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @@@ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | :@@@@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + | :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@::: + 0 +----------------------------------------------------------------------->Gi + 0 7.882""" im = hopper("RGB") for _ in range(iterations): diff --git a/Tests/check_libtiff_segfault.py b/Tests/check_libtiff_segfault.py index 5a6116f4f6a..bd7f407e4ab 100644 --- a/Tests/check_libtiff_segfault.py +++ b/Tests/check_libtiff_segfault.py @@ -6,9 +6,9 @@ def test_libtiff_segfault(): - """ This test should not segfault. It will on Pillow <= 3.1.0 and - libtiff >= 4.0.0 - """ + """This test should not segfault. It will on Pillow <= 3.1.0 and + libtiff >= 4.0.0 + """ with pytest.raises(OSError): with Image.open(TEST_FILE) as im: diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index 496eb697e66..11962925631 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -16,8 +16,8 @@ def get_files(d, ext=".bmp"): def test_bad(): - """ These shouldn't crash/dos, but they shouldn't return anything - either """ + """These shouldn't crash/dos, but they shouldn't return anything + either""" for f in get_files("b"): def open(f): @@ -32,8 +32,8 @@ def open(f): def test_questionable(): - """ These shouldn't crash/dos, but it's not well defined that these - are in spec """ + """These shouldn't crash/dos, but it's not well defined that these + are in spec""" supported = [ "pal8os2v2.bmp", "rgb24prof.bmp", @@ -57,8 +57,8 @@ def test_questionable(): def test_good(): - """ These should all work. There's a set of target files in the - html directory that we can compare against. """ + """These should all work. There's a set of target files in the + html directory that we can compare against.""" # Target files, if they're not just replacing the extension file_map = { diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index 4338070833f..a1dbae3a54e 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -359,7 +359,10 @@ def test_apng_save_split_fdat(tmp_path): with Image.open("Tests/images/old-style-jpeg-compression.png") as im: frames = [im.copy(), Image.new("RGBA", im.size, (255, 0, 0, 255))] im.save( - test_file, save_all=True, default_image=True, append_images=frames, + test_file, + save_all=True, + default_image=True, + append_images=frames, ) with Image.open(test_file) as im: exception = None diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 9fbff23e607..71779461465 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -39,7 +39,7 @@ def roundtrip(self, im, **options): return im def gen_random_image(self, size, mode="RGB"): - """ Generates a very hard to compress file + """Generates a very hard to compress file :param size: tuple :param mode: optional image mode @@ -99,7 +99,8 @@ def test_cmyk(self): assert k > 0.9 @pytest.mark.parametrize( - "test_image_path", [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"], + "test_image_path", + [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"], ) def test_dpi(self, test_image_path): def test(xdpi, ydpi=None): diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 9ae166bffd0..706488f8738 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -402,8 +402,8 @@ def test_g4_string_info(self, tmp_path): assert "temp.tif" == reread.tag[269][0] def test_12bit_rawmode(self): - """ Are we generating the same interpretation - of the image as Imagemagick is? """ + """Are we generating the same interpretation + of the image as Imagemagick is?""" TiffImagePlugin.READ_LIBTIFF = True with Image.open("Tests/images/12bit.cropped.tif") as im: im.load() @@ -503,9 +503,9 @@ def test_palette_save(self, tmp_path): assert len(reloaded.tag_v2[320]) == 768 def xtest_bw_compression_w_rgb(self, tmp_path): - """ This test passes, but when running all tests causes a failure due - to output on stderr from the error thrown by libtiff. We need to - capture that but not now""" + """This test passes, but when running all tests causes a failure due + to output on stderr from the error thrown by libtiff. We need to + capture that but not now""" im = hopper("RGB") out = str(tmp_path / "temp.tif") @@ -768,7 +768,12 @@ def test_16bit_RGBa_tiff(self): assert im.mode == "RGBA" assert im.size == (100, 40) assert im.tile, [ - ("libtiff", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False, 38236),) + ( + "libtiff", + (0, 0, 100, 40), + 0, + ("RGBa;16N", "tiff_lzw", False, 38236), + ) ] im.load() diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py index 593a8eda83f..03137c8b603 100644 --- a/Tests/test_file_libtiff_small.py +++ b/Tests/test_file_libtiff_small.py @@ -7,13 +7,13 @@ class TestFileLibTiffSmall(LibTiffTestCase): - """ The small lena image was failing on open in the libtiff - decoder because the file pointer was set to the wrong place - by a spurious seek. It wasn't failing with the byteio method. + """The small lena image was failing on open in the libtiff + decoder because the file pointer was set to the wrong place + by a spurious seek. It wasn't failing with the byteio method. - It was fixed by forcing an lseek to the beginning of the - file just before reading in libtiff. These tests remain - to ensure that it stays fixed. """ + It was fixed by forcing an lseek to the beginning of the + file just before reading in libtiff. These tests remain + to ensure that it stays fixed.""" def test_g4_hopper_file(self, tmp_path): """Testing the open file load path""" diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index aefe6f9eaaa..59411504219 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -225,8 +225,8 @@ def test_16bit_s(self): assert im.getpixel((0, 1)) == 0 def test_12bit_rawmode(self): - """ Are we generating the same interpretation - of the image as Imagemagick is? """ + """Are we generating the same interpretation + of the image as Imagemagick is?""" with Image.open("Tests/images/12bit.cropped.tif") as im: # to make the target -- diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 54a1f416358..4b3d8ded3dc 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -12,10 +12,10 @@ def test_rt_metadata(tmp_path): - """ Test writing arbitrary metadata into the tiff image directory - Use case is ImageJ private tags, one numeric, one arbitrary - data. https://github.com/python-pillow/Pillow/issues/291 - """ + """Test writing arbitrary metadata into the tiff image directory + Use case is ImageJ private tags, one numeric, one arbitrary + data. https://github.com/python-pillow/Pillow/issues/291 + """ img = hopper() diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index d10b1acfd66..3b9c8b07146 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -85,7 +85,10 @@ def test_wedge(): im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" ) assert_image_similar( - im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong", + im.getchannel(1), + comparable.getchannel(1), + 1, + "Saturation conversion is wrong", ) assert_image_similar( im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" @@ -113,7 +116,10 @@ def test_convert(): im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" ) assert_image_similar( - im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong", + im.getchannel(1), + comparable.getchannel(1), + 1, + "Saturation conversion is wrong", ) assert_image_similar( im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" @@ -126,11 +132,20 @@ def test_hsv_to_rgb(): comparable = to_rgb_colorsys(comparable) assert_image_similar( - converted.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong", + converted.getchannel(0), + comparable.getchannel(0), + 3, + "R conversion is wrong", ) assert_image_similar( - converted.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong", + converted.getchannel(1), + comparable.getchannel(1), + 3, + "G conversion is wrong", ) assert_image_similar( - converted.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong", + converted.getchannel(2), + comparable.getchannel(2), + 3, + "B conversion is wrong", ) diff --git a/Tests/test_image.py b/Tests/test_image.py index cc0edbdd7a7..6d188e7405a 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -707,7 +707,8 @@ def test_exif_interop(self): } @pytest.mark.parametrize( - "test_module", [PIL, Image], + "test_module", + [PIL, Image], ) def test_pillow_version(self, test_module): with pytest.warns(DeprecationWarning): @@ -735,7 +736,7 @@ def test_pillow_version(self, test_module): assert test_module.PILLOW_VERSION > "7.0.0" def test_overrun(self): - """ For overrun completeness, test as: + """For overrun completeness, test as: valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c """ for file in [ diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 4c0dbb4ccfe..e5dad235f47 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -132,9 +132,11 @@ def check(self, mode, c=None): # check putpixel negative index im.putpixel((-1, -1), c) - assert im.getpixel((-1, -1)) == c, ( - "put/getpixel roundtrip negative index failed for mode %s, color %s" - % (mode, c) + assert ( + im.getpixel((-1, -1)) == c + ), "put/getpixel roundtrip negative index failed for mode %s, color %s" % ( + mode, + c, ) # Check 0 diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index fe868b7c2fe..51108ead2fd 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -24,9 +24,9 @@ def test_sanity(): def test_16bit_lut(): - """ Tests for 16 bit -> 8 bit lut for converting I->L images - see https://github.com/python-pillow/Pillow/issues/440 - """ + """Tests for 16 bit -> 8 bit lut for converting I->L images + see https://github.com/python-pillow/Pillow/issues/440 + """ im = hopper("I") im.point(list(range(256)) * 256, "L") diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 0c15b20844e..e9149b84332 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -437,7 +437,7 @@ def truncate_tuple(tuple_or_float): def test_profile_typesafety(): - """ Profile init type safety + """Profile init type safety prepatch, these would segfault, postpatch they should emit a typeerror """ diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 224914b9474..cc8bd24383c 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -667,7 +667,10 @@ def test_floodfill_border(): # Act ImageDraw.floodfill( - im, centre_point, ImageColor.getrgb("red"), border=ImageColor.getrgb("black"), + im, + centre_point, + ImageColor.getrgb("red"), + border=ImageColor.getrgb("black"), ) # Assert diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index 337d2096e01..0ba6828858d 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -34,6 +34,9 @@ def test_similar(): (0, size_final[1] - size_bitmap[1]), text, fill=(0, 0, 0), font=font_bitmap ) draw_outline.text( - (0, size_final[1] - size_outline[1]), text, fill=(0, 0, 0), font=font_outline, + (0, size_final[1] - size_outline[1]), + text, + fill=(0, 0, 0), + font=font_outline, ) assert_image_similar(im_bitmap, im_outline, 20) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 043a7aaaec5..78e80f521d6 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -19,7 +19,8 @@ def test_register(): @pytest.mark.parametrize( - "order", [-1, 0], + "order", + [-1, 0], ) def test_viewer_show(order): class TestViewer(ImageShow.Viewer): @@ -41,7 +42,8 @@ def show_image(self, image, **options): @pytest.mark.skipif( - not on_ci() or is_win32(), reason="Only run on CIs; hangs on Windows CIs", + not on_ci() or is_win32(), + reason="Only run on CIs; hangs on Windows CIs", ) def test_show(): for mode in ("1", "I;16", "LA", "RGB", "RGBA"): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 00adf2d0aa3..9d1589078ca 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -665,7 +665,7 @@ def __repr__(self): ) def _repr_png_(self): - """ iPython display hook support + """iPython display hook support :returns: png version of the image as bytes """ @@ -1181,7 +1181,7 @@ def filter(self, filter): available filters, see the :py:mod:`~PIL.ImageFilter` module. :param filter: Filter kernel. - :returns: An :py:class:`~PIL.Image.Image` object. """ + :returns: An :py:class:`~PIL.Image.Image` object.""" from . import ImageFilter @@ -1506,7 +1506,7 @@ def paste(self, im, box=None, mask=None): self.im.paste(im, box) def alpha_composite(self, im, dest=(0, 0), source=(0, 0)): - """ 'In-place' analog of Image.alpha_composite. Composites an image + """'In-place' analog of Image.alpha_composite. Composites an image onto this image. :param im: image to composite over this one diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 8155a0b2d8d..1b4a11e48a3 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -277,8 +277,8 @@ def get_display_profile(handle=None): class PyCMSError(Exception): - """ (pyCMS) Exception class. - This is used for all errors in the pyCMS API. """ + """(pyCMS) Exception class. + This is used for all errors in the pyCMS API.""" pass diff --git a/src/PIL/ImageMorph.py b/src/PIL/ImageMorph.py index d1ec09eace4..b76dfa01f4d 100644 --- a/src/PIL/ImageMorph.py +++ b/src/PIL/ImageMorph.py @@ -28,36 +28,36 @@ class LutBuilder: """A class for building a MorphLut from a descriptive language - The input patterns is a list of a strings sequences like these:: + The input patterns is a list of a strings sequences like these:: - 4:(... - .1. - 111)->1 + 4:(... + .1. + 111)->1 - (whitespaces including linebreaks are ignored). The option 4 - describes a series of symmetry operations (in this case a - 4-rotation), the pattern is described by: + (whitespaces including linebreaks are ignored). The option 4 + describes a series of symmetry operations (in this case a + 4-rotation), the pattern is described by: - - . or X - Ignore - - 1 - Pixel is on - - 0 - Pixel is off + - . or X - Ignore + - 1 - Pixel is on + - 0 - Pixel is off - The result of the operation is described after "->" string. + The result of the operation is described after "->" string. - The default is to return the current pixel value, which is - returned if no other match is found. + The default is to return the current pixel value, which is + returned if no other match is found. - Operations: + Operations: - - 4 - 4 way rotation - - N - Negate - - 1 - Dummy op for no other operation (an op must always be given) - - M - Mirroring + - 4 - 4 way rotation + - N - Negate + - 1 - Dummy op for no other operation (an op must always be given) + - M - Mirroring - Example:: + Example:: - lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) - lut = lb.build_lut() + lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) + lut = lb.build_lut() """ diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 192c94c89d9..ae42365b458 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1110,7 +1110,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode): # animation control chunk( - fp, b"acTL", o32(len(im_frames)), o32(loop), # 0: num_frames # 4: num_plays + fp, + b"acTL", + o32(len(im_frames)), + o32(loop), # 0: num_frames # 4: num_plays ) # default image IDAT (if it exists) @@ -1155,7 +1158,9 @@ def _write_multiple_frames(im, fp, chunk, rawmode): else: fdat_chunks = _fdat(fp, chunk, seq_num) ImageFile._save( - im_frame, fdat_chunks, [("zip", (0, 0) + im_frame.size, 0, rawmode)], + im_frame, + fdat_chunks, + [("zip", (0, 0) + im_frame.size, 0, rawmode)], ) seq_num = fdat_chunks.seq_num diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index cb925246d47..57f08d98c6d 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -286,7 +286,7 @@ def _limit_signed_rational(val, max_val, min_val): class IFDRational(Rational): - """ Implements a rational class where 0/0 is a legal value to match + """Implements a rational class where 0/0 is a legal value to match the in the wild use of exif rationals. e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used @@ -907,7 +907,7 @@ def __init__(self, *args, **kwargs): @classmethod def from_v2(cls, original): - """ Returns an + """Returns an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` instance with the same data as is contained in the original :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` @@ -924,7 +924,7 @@ def from_v2(cls, original): return ifd def to_v2(self): - """ Returns an + """Returns an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` instance with the same data as is contained in the original :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` @@ -1094,8 +1094,8 @@ def load_end(self): self._close_exclusive_fp_after_loading = True def _load_libtiff(self): - """ Overload method triggered when we detect a compressed tiff - Calls out to libtiff """ + """Overload method triggered when we detect a compressed tiff + Calls out to libtiff""" Image.Image.load(self) From bb013270af0cccd7afc53a9105886b6e8d7f2579 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 31 Aug 2020 18:46:42 +1000 Subject: [PATCH 42/45] Removed trailing comma --- Tests/test_file_libtiff.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 706488f8738..2d89bd79e0f 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -768,12 +768,7 @@ def test_16bit_RGBa_tiff(self): assert im.mode == "RGBA" assert im.size == (100, 40) assert im.tile, [ - ( - "libtiff", - (0, 0, 100, 40), - 0, - ("RGBa;16N", "tiff_lzw", False, 38236), - ) + ("libtiff", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False, 38236)) ] im.load() From ca43774d4039188175dcffab75c3e05a0850036d Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 31 Aug 2020 18:48:49 +1000 Subject: [PATCH 43/45] Corrected comment layout Co-authored-by: Hugo van Kemenade --- src/PIL/PngImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index ae42365b458..0f1515d6db3 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1112,8 +1112,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode): chunk( fp, b"acTL", - o32(len(im_frames)), - o32(loop), # 0: num_frames # 4: num_plays + o32(len(im_frames)), # 0: num_frames + o32(loop), # 4: num_plays ) # default image IDAT (if it exists) From 799b25d9f1889c15e74ffcafe961513f73a900a3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 31 Aug 2020 22:44:51 +1000 Subject: [PATCH 44/45] Renamed zip header file --- src/decode.c | 2 +- src/encode.c | 2 +- src/libImaging/{ZipState.h => ZipCodecs.h} | 0 src/libImaging/ZipDecode.c | 2 +- src/libImaging/ZipEncode.c | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/libImaging/{ZipState.h => ZipCodecs.h} (100%) diff --git a/src/decode.c b/src/decode.c index 3d7eb5eabd3..f60fb490e26 100644 --- a/src/decode.c +++ b/src/decode.c @@ -807,7 +807,7 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) #ifdef HAVE_LIBZ -#include "ZipState.h" +#include "ZipCodecs.h" PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) diff --git a/src/encode.c b/src/encode.c index e9a34418701..62a6ec38706 100644 --- a/src/encode.c +++ b/src/encode.c @@ -578,7 +578,7 @@ PyImaging_XbmEncoderNew(PyObject* self, PyObject* args) #ifdef HAVE_LIBZ -#include "ZipState.h" +#include "ZipCodecs.h" PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) diff --git a/src/libImaging/ZipState.h b/src/libImaging/ZipCodecs.h similarity index 100% rename from src/libImaging/ZipState.h rename to src/libImaging/ZipCodecs.h diff --git a/src/libImaging/ZipDecode.c b/src/libImaging/ZipDecode.c index f9b12db7030..a09ee82f7cc 100644 --- a/src/libImaging/ZipDecode.c +++ b/src/libImaging/ZipDecode.c @@ -20,7 +20,7 @@ #ifdef HAVE_LIBZ -#include "ZipState.h" +#include "ZipCodecs.h" static const int OFFSET[] = { 7, 3, 3, 1, 1, 0, 0 }; static const int STARTING_COL[] = { 0, 4, 0, 2, 0, 1, 0 }; diff --git a/src/libImaging/ZipEncode.c b/src/libImaging/ZipEncode.c index 67b7acbf824..4e862af57a7 100644 --- a/src/libImaging/ZipEncode.c +++ b/src/libImaging/ZipEncode.c @@ -19,7 +19,7 @@ #ifdef HAVE_LIBZ -#include "ZipState.h" +#include "ZipCodecs.h" int ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) From 2a194a63236cdfe84204227c08afab3dd6379a89 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 31 Aug 2020 11:31:35 -0700 Subject: [PATCH 45/45] Remove W503 from flake8 ignore list (ignored by default) See: https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes > In the default configuration, the checks E121, E123, E126, E133, E226, > E241, E242, E704, W503, W504 and W505 are ignored because they are not > rules unanimously accepted, and PEP 8 does not enforce them. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b91a522f59a..129adeee7a6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [flake8] -extend-ignore = E203, W503 +extend-ignore = E203 max-line-length = 88 [isort]