From e75f2989b40c19f9b3e7bc895bbf75f21dcd92d7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 22 Jul 2022 10:36:01 +0000 Subject: [PATCH] [FIX] core, web: Pillow 9.1 deprecations Pillow 9.1 deprecates most if not all toplevel Image constants: https://pillow.readthedocs.io/en/stable/releasenotes/9.1.0.html#constants These constants have been moved to thematic enum classes (e.g. all the resampling constants in `PIL.Image.Resampling`). This triggers warnings in Odoo, and the removal delay is quite short (slated for Pillow 10, release planned mid 2023). Pillow 9.1 is also already in Debian Bookworm (current testing). Fix by shimming at the import level: if the enums are available import them into the local namespace, otherwise alias `PIL.Image` itself as to the enum. closes odoo/odoo#96799 X-original-commit: 7be04d31bad078681ef0a2919234c11840a2e8e2 Signed-off-by: Xavier Morel (xmo) --- addons/web/models/base_document_layout.py | 7 ++++-- odoo/tools/image.py | 28 +++++++++++++---------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/addons/web/models/base_document_layout.py b/addons/web/models/base_document_layout.py index 36d8a511dfc..ff685b32073 100644 --- a/addons/web/models/base_document_layout.py +++ b/addons/web/models/base_document_layout.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import markupsafe -from PIL import Image from markupsafe import Markup from odoo import api, fields, models, tools @@ -14,6 +13,10 @@ except ImportError: # If the `sass` python library isn't found, we fallback on the # `sassc` executable in the path. libsass = None +try: + from PIL.Image import Resampling +except ImportError: + from PIL import Image as Resampling DEFAULT_PRIMARY = '#000000' DEFAULT_SECONDARY = '#000000' @@ -204,7 +207,7 @@ class BaseDocumentLayout(models.TransientModel): # Converts to RGBA (if already RGBA, this is a noop) image_converted = image.convert('RGBA') - image_resized = image_converted.resize((w, h), resample=Image.NEAREST) + image_resized = image_converted.resize((w, h), resample=Resampling.NEAREST) colors = [] for color in image_resized.getcolors(w * h): diff --git a/odoo/tools/image.py b/odoo/tools/image.py index 55ef4690b8f..8db8ada4b9b 100644 --- a/odoo/tools/image.py +++ b/odoo/tools/image.py @@ -7,6 +7,10 @@ import io from PIL import Image, ImageOps # We can preload Ico too because it is considered safe from PIL import IcoImagePlugin +try: + from PIL.Image import Transpose, Palette, Resampling +except ImportError: + Transpose = Palette = Resampling = Image from random import randrange @@ -30,16 +34,16 @@ FILETYPE_BASE64_MAGICWORD = { EXIF_TAG_ORIENTATION = 0x112 # The target is to have 1st row/col to be top/left # Note: rotate is counterclockwise -EXIF_TAG_ORIENTATION_TO_TRANSPOSE_METHODS = { # Initial side on 1st row/col: - 0: [], # reserved - 1: [], # top/left - 2: [Image.FLIP_LEFT_RIGHT], # top/right - 3: [Image.ROTATE_180], # bottom/right - 4: [Image.FLIP_TOP_BOTTOM], # bottom/left - 5: [Image.FLIP_LEFT_RIGHT, Image.ROTATE_90], # left/top - 6: [Image.ROTATE_270], # right/top - 7: [Image.FLIP_TOP_BOTTOM, Image.ROTATE_90], # right/bottom - 8: [Image.ROTATE_90], # left/bottom +EXIF_TAG_ORIENTATION_TO_TRANSPOSE_METHODS = { # Initial side on 1st row/col: + 0: [], # reserved + 1: [], # top/left + 2: [Transpose.FLIP_LEFT_RIGHT], # top/right + 3: [Transpose.ROTATE_180], # bottom/right + 4: [Transpose.FLIP_TOP_BOTTOM], # bottom/left + 5: [Transpose.FLIP_LEFT_RIGHT, Transpose.ROTATE_90],# left/top + 6: [Transpose.ROTATE_270], # right/top + 7: [Transpose.FLIP_TOP_BOTTOM, Transpose.ROTATE_90],# right/bottom + 8: [Transpose.ROTATE_90], # left/bottom } # Arbitrary limit to fit most resolutions, including Nokia Lumia 1020 photo, @@ -134,7 +138,7 @@ class ImageProcess(): if quality: if output_image.mode != 'P': # Floyd Steinberg dithering by default - output_image = output_image.convert('RGBA').convert('P', palette=Image.WEB, colors=256) + output_image = output_image.convert('RGBA').convert('P', palette=Palette.WEB, colors=256) if output_format == 'JPEG': opt['optimize'] = True opt['quality'] = quality or 95 @@ -171,7 +175,7 @@ class ImageProcess(): asked_width = max_width or (w * max_height) // h asked_height = max_height or (h * max_width) // w if asked_width != w or asked_height != h: - self.image.thumbnail((asked_width, asked_height), Image.LANCZOS) + self.image.thumbnail((asked_width, asked_height), Resampling.LANCZOS) if self.image.width != w or self.image.height != h: self.operationsCount += 1 return self