Commit c22cfd5e authored by Jérome Perrin's avatar Jérome Perrin Committed by Rafael Monnerat

WIP: patches/python: override py3 builtin round to keep py2 behavior.

And workaround safeimage impacted by this round2 patch.
parent 165434ea
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
############################################################################## ##############################################################################
import six import six
import contextlib
import os, sys, shutil, tempfile import os, sys, shutil, tempfile
import io import io
from zLOG import LOG,ERROR,INFO,WARNING from zLOG import LOG,ERROR,INFO,WARNING
...@@ -33,6 +34,27 @@ import base64 ...@@ -33,6 +34,27 @@ import base64
from OFS.Folder import Folder from OFS.Folder import Folder
from Products.ERP5Type.Utils import str2bytes from Products.ERP5Type.Utils import str2bytes
# XXX zope4py3, we patch builtin round to keep python2 behavior
# but this interfers with PIL, because python2 round sometimes
# returns an int where python3 round is supposed to return a float.
# This context manager swaps the implementation with the native
# float on python3, which is NOT THREAD SAFE. Don't use it in
# production, for now this makes the test pass 🤐.
import Products.ERP5Type.patches.python
@contextlib.contextmanager
def native_round():
if six.PY3:
round_original = __builtins__['round']
try:
__builtins__['round'] = Products.ERP5Type.patches.python.round_native
yield
finally:
__builtins__['round'] = round_original
else:
# on python2 do nothing
yield
class ZoomifyBase: class ZoomifyBase:
_v_imageFilename = '' _v_imageFilename = ''
...@@ -180,6 +202,7 @@ class ZoomifyBase: ...@@ -180,6 +202,7 @@ class ZoomifyBase:
saveFilename = root + str(tier) + '-' + str(row) + ext saveFilename = root + str(tier) + '-' + str(row) + ext
if imageRow.mode != 'RGB': if imageRow.mode != 'RGB':
imageRow = imageRow.convert('RGB') imageRow = imageRow.convert('RGB')
with native_round():
imageRow.save(os.path.join(tempfile.gettempdir(), saveFilename), imageRow.save(os.path.join(tempfile.gettempdir(), saveFilename),
# see https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg-saving # see https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg-saving
# for quality, Values above 95 should be avoided; # for quality, Values above 95 should be avoided;
...@@ -263,6 +286,7 @@ class ZoomifyBase: ...@@ -263,6 +286,7 @@ class ZoomifyBase:
(imageWidth//2, imageHeight//2), (imageWidth//2, imageHeight//2),
PIL_Image.LANCZOS if six.PY3 else PIL_Image.ANTIALIAS, PIL_Image.LANCZOS if six.PY3 else PIL_Image.ANTIALIAS,
) )
with native_round():
tempImage.save(os.path.join(tempfile.gettempdir(), root + str(tier) tempImage.save(os.path.join(tempfile.gettempdir(), root + str(tier)
+ '-' + str(row) + ext)) + '-' + str(row) + ext))
tempImage = None tempImage = None
...@@ -542,6 +566,7 @@ class ERP5ZoomifyZopeProcessor(ZoomifyZopeProcessor): ...@@ -542,6 +566,7 @@ class ERP5ZoomifyZopeProcessor(ZoomifyZopeProcessor):
tile_group_id = self.getAssignedTileContainerName() tile_group_id = self.getAssignedTileContainerName()
tile_group=self.document[tile_group_id] tile_group=self.document[tile_group_id]
tileImageData= io.BytesIO() tileImageData= io.BytesIO()
with native_round():
image.save(tileImageData, 'JPEG', quality=self.qualitySetting) image.save(tileImageData, 'JPEG', quality=self.qualitySetting)
tileImageData.seek(0) tileImageData.seek(0)
if tile_group is None: if tile_group is None:
......
...@@ -178,3 +178,43 @@ def patch_linecache(): ...@@ -178,3 +178,43 @@ def patch_linecache():
if sys.version_info[:3] < (3, ): if sys.version_info[:3] < (3, ):
patch_linecache() patch_linecache()
import decimal as _decimal
def round2(number, ndigits=None):
"""
See Python 2 documentation.
Rounds a number to a given precision in decimal digits (default
0 digits). The result is a floating point number. Values are rounded
to the closest multiple of 10 to the power minus ndigits; if two
multiples are equally close, rounding is done away from 0.
ndigits may be negative.
"""
if ndigits is None:
ndigits = 0
elif hasattr(ndigits, '__index__'):
# any type with an __index__ method should be permitted as
# a second argument
ndigits = ndigits.__index__()
if ndigits < 0:
exponent = 10 ** (-ndigits)
quotient, remainder = divmod(number, exponent)
if remainder >= exponent//2 and number >= 0:
quotient += 1
return float(quotient * exponent)
else:
exponent = _decimal.Decimal('10') ** (-ndigits)
d = _decimal.Decimal.from_float(number).quantize(
exponent, rounding=_decimal.ROUND_HALF_UP)
return float(d)
round_native = round
if sys.version_info > (2, ):
__builtins__['round'] = round2
from AccessControl.ZopeGuards import safe_builtins
safe_builtins['round'] = round2
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment