mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-04-28 13:03:43 +03:00
Merge pull request #5768 from hugovk/rm-3.6
Drop support for soon-EOL Python 3.6
This commit is contained in:
commit
a70e3c828f
|
@ -13,7 +13,7 @@ environment:
|
||||||
- PYTHON: C:/Python310
|
- PYTHON: C:/Python310
|
||||||
ARCHITECTURE: x86
|
ARCHITECTURE: x86
|
||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||||
- PYTHON: C:/Python36-x64
|
- PYTHON: C:/Python37-x64
|
||||||
ARCHITECTURE: x64
|
ARCHITECTURE: x64
|
||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ repos:
|
||||||
rev: 911470a610e47d9da5ea938b0887c3df62819b85 # frozen: 21.9b0
|
rev: 911470a610e47d9da5ea938b0887c3df62819b85 # frozen: 21.9b0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args: ["--target-version", "py36"]
|
args: ["--target-version", "py37"]
|
||||||
# Only .py files, until https://github.com/psf/black/issues/402 resolved
|
# Only .py files, until https://github.com/psf/black/issues/402 resolved
|
||||||
files: \.py$
|
files: \.py$
|
||||||
types: []
|
types: []
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -122,5 +122,5 @@ lint:
|
||||||
|
|
||||||
.PHONY: lint-fix
|
.PHONY: lint-fix
|
||||||
lint-fix:
|
lint-fix:
|
||||||
black --target-version py36 .
|
black --target-version py37 .
|
||||||
isort .
|
isort .
|
||||||
|
|
|
@ -818,7 +818,7 @@ def test_palette_save_P(tmp_path):
|
||||||
# Forcing a non-straight grayscale palette.
|
# Forcing a non-straight grayscale palette.
|
||||||
|
|
||||||
im = hopper("P")
|
im = hopper("P")
|
||||||
palette = bytes([255 - i // 3 for i in range(768)])
|
palette = bytes(255 - i // 3 for i in range(768))
|
||||||
|
|
||||||
out = str(tmp_path / "temp.gif")
|
out = str(tmp_path / "temp.gif")
|
||||||
im.save(out, palette=palette)
|
im.save(out, palette=palette)
|
||||||
|
@ -885,7 +885,7 @@ def test_getdata():
|
||||||
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
||||||
im.info = {"background": 0}
|
im.info = {"background": 0}
|
||||||
|
|
||||||
passed_palette = bytes([255 - i // 3 for i in range(768)])
|
passed_palette = bytes(255 - i // 3 for i in range(768))
|
||||||
|
|
||||||
GifImagePlugin._FORCE_OPTIMIZE = True
|
GifImagePlugin._FORCE_OPTIMIZE = True
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -85,26 +85,26 @@ class TestFileJpeg:
|
||||||
f = "Tests/images/pil_sample_cmyk.jpg"
|
f = "Tests/images/pil_sample_cmyk.jpg"
|
||||||
with Image.open(f) as im:
|
with Image.open(f) as im:
|
||||||
# the source image has red pixels in the upper left corner.
|
# the source image has red pixels in the upper left corner.
|
||||||
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
|
c, m, y, k = (x / 255.0 for x in im.getpixel((0, 0)))
|
||||||
assert c == 0.0
|
assert c == 0.0
|
||||||
assert m > 0.8
|
assert m > 0.8
|
||||||
assert y > 0.8
|
assert y > 0.8
|
||||||
assert k == 0.0
|
assert k == 0.0
|
||||||
# the opposite corner is black
|
# the opposite corner is black
|
||||||
c, m, y, k = [
|
c, m, y, k = (
|
||||||
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||||
]
|
)
|
||||||
assert k > 0.9
|
assert k > 0.9
|
||||||
# roundtrip, and check again
|
# roundtrip, and check again
|
||||||
im = self.roundtrip(im)
|
im = self.roundtrip(im)
|
||||||
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
|
c, m, y, k = (x / 255.0 for x in im.getpixel((0, 0)))
|
||||||
assert c == 0.0
|
assert c == 0.0
|
||||||
assert m > 0.8
|
assert m > 0.8
|
||||||
assert y > 0.8
|
assert y > 0.8
|
||||||
assert k == 0.0
|
assert k == 0.0
|
||||||
c, m, y, k = [
|
c, m, y, k = (
|
||||||
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||||
]
|
)
|
||||||
assert k > 0.9
|
assert k > 0.9
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -188,9 +188,7 @@ class TestFileWebp:
|
||||||
|
|
||||||
with Image.open(out_gif) as reread:
|
with Image.open(out_gif) as reread:
|
||||||
reread_value = reread.convert("RGB").getpixel((1, 1))
|
reread_value = reread.convert("RGB").getpixel((1, 1))
|
||||||
difference = sum(
|
difference = sum(abs(original_value[i] - reread_value[i]) for i in range(0, 3))
|
||||||
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
|
||||||
)
|
|
||||||
assert difference < 5
|
assert difference < 5
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
@skip_unless_feature("webp")
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -782,9 +781,6 @@ class TestImage:
|
||||||
34665: 196,
|
34665: 196,
|
||||||
}
|
}
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 7), reason="Python 3.7 or greater required"
|
|
||||||
)
|
|
||||||
def test_categories_deprecation(self):
|
def test_categories_deprecation(self):
|
||||||
with pytest.warns(DeprecationWarning):
|
with pytest.warns(DeprecationWarning):
|
||||||
assert hopper().category == 0
|
assert hopper().category == 0
|
||||||
|
|
|
@ -900,7 +900,7 @@ class TestImageFont:
|
||||||
d.text((10, 10), "\U0001f469", font=font, embedded_color=True)
|
d.text((10, 10), "\U0001f469", font=font, embedded_color=True)
|
||||||
|
|
||||||
assert_image_similar_tofile(im, "Tests/images/cbdt_notocoloremoji.png", 6.2)
|
assert_image_similar_tofile(im, "Tests/images/cbdt_notocoloremoji.png", 6.2)
|
||||||
except IOError as e: # pragma: no cover
|
except OSError as e: # pragma: no cover
|
||||||
assert str(e) in ("unimplemented feature", "unknown file format")
|
assert str(e) in ("unimplemented feature", "unknown file format")
|
||||||
pytest.skip("freetype compiled without libpng or CBDT support")
|
pytest.skip("freetype compiled without libpng or CBDT support")
|
||||||
|
|
||||||
|
@ -920,7 +920,7 @@ class TestImageFont:
|
||||||
assert_image_similar_tofile(
|
assert_image_similar_tofile(
|
||||||
im, "Tests/images/cbdt_notocoloremoji_mask.png", 6.2
|
im, "Tests/images/cbdt_notocoloremoji_mask.png", 6.2
|
||||||
)
|
)
|
||||||
except IOError as e: # pragma: no cover
|
except OSError as e: # pragma: no cover
|
||||||
assert str(e) in ("unimplemented feature", "unknown file format")
|
assert str(e) in ("unimplemented feature", "unknown file format")
|
||||||
pytest.skip("freetype compiled without libpng or CBDT support")
|
pytest.skip("freetype compiled without libpng or CBDT support")
|
||||||
|
|
||||||
|
@ -938,7 +938,7 @@ class TestImageFont:
|
||||||
d.text((50, 50), "\uE901", font=font, embedded_color=True)
|
d.text((50, 50), "\uE901", font=font, embedded_color=True)
|
||||||
|
|
||||||
assert_image_similar_tofile(im, "Tests/images/chromacheck-sbix.png", 1)
|
assert_image_similar_tofile(im, "Tests/images/chromacheck-sbix.png", 1)
|
||||||
except IOError as e: # pragma: no cover
|
except OSError as e: # pragma: no cover
|
||||||
assert str(e) in ("unimplemented feature", "unknown file format")
|
assert str(e) in ("unimplemented feature", "unknown file format")
|
||||||
pytest.skip("freetype compiled without libpng or SBIX support")
|
pytest.skip("freetype compiled without libpng or SBIX support")
|
||||||
|
|
||||||
|
@ -956,7 +956,7 @@ class TestImageFont:
|
||||||
d.text((50, 50), "\uE901", (100, 0, 0), font=font)
|
d.text((50, 50), "\uE901", (100, 0, 0), font=font)
|
||||||
|
|
||||||
assert_image_similar_tofile(im, "Tests/images/chromacheck-sbix_mask.png", 1)
|
assert_image_similar_tofile(im, "Tests/images/chromacheck-sbix_mask.png", 1)
|
||||||
except IOError as e: # pragma: no cover
|
except OSError as e: # pragma: no cover
|
||||||
assert str(e) in ("unimplemented feature", "unknown file format")
|
assert str(e) in ("unimplemented feature", "unknown file format")
|
||||||
pytest.skip("freetype compiled without libpng or SBIX support")
|
pytest.skip("freetype compiled without libpng or SBIX support")
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from PIL import Image, ImageMath
|
||||||
|
|
||||||
def pixel(im):
|
def pixel(im):
|
||||||
if hasattr(im, "im"):
|
if hasattr(im, "im"):
|
||||||
return "{} {}".format(im.mode, repr(im.getpixel((0, 0))))
|
return f"{im.mode} {repr(im.getpixel((0, 0)))}"
|
||||||
else:
|
else:
|
||||||
if isinstance(im, int):
|
if isinstance(im, int):
|
||||||
return int(im) # hack to deal with booleans
|
return int(im) # hack to deal with booleans
|
||||||
|
|
|
@ -18,7 +18,9 @@ Pillow supports these Python versions.
|
||||||
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
| Python |3.10 | 3.9 | 3.8 | 3.7 | 3.6 | 3.5 | 3.4 | 2.7 |
|
| Python |3.10 | 3.9 | 3.8 | 3.7 | 3.6 | 3.5 | 3.4 | 2.7 |
|
||||||
+======================+=====+=====+=====+=====+=====+=====+=====+=====+
|
+======================+=====+=====+=====+=====+=====+=====+=====+=====+
|
||||||
| Pillow >= 8.3.2 | Yes | Yes | Yes | Yes | Yes | | | |
|
| Pillow >= 9.0 | Yes | Yes | Yes | Yes | | | | |
|
||||||
|
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
| Pillow 8.3.2 - 8.4 | Yes | Yes | Yes | Yes | Yes | | | |
|
||||||
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
| Pillow 8.0 - 8.3.1 | | Yes | Yes | Yes | Yes | | | |
|
| Pillow 8.0 - 8.3.1 | | Yes | Yes | Yes | Yes | | | |
|
||||||
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
@ -443,42 +445,42 @@ Continuous Integration Targets
|
||||||
|
|
||||||
These platforms are built and tested for every change.
|
These platforms are built and tested for every change.
|
||||||
|
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Operating system | Tested Python versions | Tested architecture |
|
| Operating system | Tested Python versions | Tested architecture |
|
||||||
+==================================+=================================+=====================+
|
+==================================+============================+=====================+
|
||||||
| Alpine | 3.9 | x86-64 |
|
| Alpine | 3.9 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Amazon Linux 2 | 3.7 | x86-64 |
|
| Amazon Linux 2 | 3.7 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Arch | 3.9 | x86-64 |
|
| Arch | 3.9 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| CentOS 7 | 3.6 | x86-64 |
|
| CentOS 7 | 3.9 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| CentOS 8 | 3.6 | x86-64 |
|
| CentOS 8 | 3.9 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| CentOS Stream 8 | 3.6 | x86-64 |
|
| CentOS Stream 8 | 3.9 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Debian 10 Buster | 3.7 | x86 |
|
| Debian 10 Buster | 3.7 | x86 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Fedora 34 | 3.9 | x86-64 |
|
| Fedora 34 | 3.9 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Fedora 35 | 3.10 | x86-64 |
|
| Fedora 35 | 3.10 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 |
|
| macOS 10.15 Catalina | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Ubuntu Linux 18.04 LTS (Bionic) | 3.6 | x86-64 |
|
| Ubuntu Linux 18.04 LTS (Bionic) | 3.9 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Ubuntu Linux 20.04 LTS (Focal) | 3.6, 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 |
|
| Ubuntu Linux 20.04 LTS (Focal) | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 |
|
||||||
| +---------------------------------+---------------------+
|
| +----------------------------+---------------------+
|
||||||
| | 3.8 | arm64v8, ppc64le, |
|
| | 3.8 | arm64v8, ppc64le, |
|
||||||
| | | s390x |
|
| | | s390x |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Windows Server 2016 | 3.6 | x86-64 |
|
| Windows Server 2016 | 3.7 | x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Windows Server 2019 | 3.6, 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 |
|
| Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 |
|
||||||
| +---------------------------------+---------------------+
|
| +----------------------------+---------------------+
|
||||||
| | 3.9/MinGW | x86, x86-64 |
|
| | 3.9/MinGW | x86, x86-64 |
|
||||||
+----------------------------------+---------------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
|
|
||||||
|
|
||||||
Other Platforms
|
Other Platforms
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
Backwards Incompatible Changes
|
Backwards Incompatible Changes
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
Python 3.6
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
Pillow has dropped support for Python 3.6, which reached end-of-life on 2021-12-23.
|
||||||
|
|
||||||
PILLOW_VERSION constant
|
PILLOW_VERSION constant
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ classifiers =
|
||||||
License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)
|
License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Programming Language :: Python :: 3 :: Only
|
Programming Language :: Python :: 3 :: Only
|
||||||
Programming Language :: Python :: 3.6
|
|
||||||
Programming Language :: Python :: 3.7
|
Programming Language :: Python :: 3.7
|
||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
|
@ -34,7 +33,7 @@ project_urls =
|
||||||
Twitter=https://twitter.com/PythonPillow
|
Twitter=https://twitter.com/PythonPillow
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
python_requires = >=3.6
|
python_requires = >=3.7
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
extend-ignore = E203
|
extend-ignore = E203
|
||||||
|
|
|
@ -324,7 +324,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
if not self.im and "transparency" in self.info:
|
if not self.im and "transparency" in self.info:
|
||||||
self.im = Image.core.fill(self.mode, self.size, self.info["transparency"])
|
self.im = Image.core.fill(self.mode, self.size, self.info["transparency"])
|
||||||
|
|
||||||
super(GifImageFile, self).load_prepare()
|
super().load_prepare()
|
||||||
|
|
||||||
def tell(self):
|
def tell(self):
|
||||||
return self.__frame
|
return self.__frame
|
||||||
|
|
|
@ -51,7 +51,6 @@ from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins
|
||||||
from ._binary import i32le
|
from ._binary import i32le
|
||||||
from ._util import deferred_error, isPath
|
from ._util import deferred_error, isPath
|
||||||
|
|
||||||
if sys.version_info >= (3, 7):
|
|
||||||
|
|
||||||
def __getattr__(name):
|
def __getattr__(name):
|
||||||
categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2}
|
categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2}
|
||||||
|
@ -66,13 +65,6 @@ if sys.version_info >= (3, 7):
|
||||||
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
# categories
|
|
||||||
NORMAL = 0
|
|
||||||
SEQUENCE = 1
|
|
||||||
CONTAINER = 2
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -926,12 +918,8 @@ class Image:
|
||||||
transparency = convert_transparency(matrix, transparency)
|
transparency = convert_transparency(matrix, transparency)
|
||||||
elif len(mode) == 3:
|
elif len(mode) == 3:
|
||||||
transparency = tuple(
|
transparency = tuple(
|
||||||
[
|
convert_transparency(matrix[i * 4 : i * 4 + 4], transparency)
|
||||||
convert_transparency(
|
|
||||||
matrix[i * 4 : i * 4 + 4], transparency
|
|
||||||
)
|
|
||||||
for i in range(0, len(transparency))
|
for i in range(0, len(transparency))
|
||||||
]
|
|
||||||
)
|
)
|
||||||
new.info["transparency"] = transparency
|
new.info["transparency"] = transparency
|
||||||
return new
|
return new
|
||||||
|
@ -1934,7 +1922,7 @@ class Image:
|
||||||
message = f"Unknown resampling filter ({resample})."
|
message = f"Unknown resampling filter ({resample})."
|
||||||
|
|
||||||
filters = [
|
filters = [
|
||||||
"{} ({})".format(filter[1], filter[0])
|
f"{filter[1]} ({filter[0]})"
|
||||||
for filter in (
|
for filter in (
|
||||||
(NEAREST, "Image.NEAREST"),
|
(NEAREST, "Image.NEAREST"),
|
||||||
(LANCZOS, "Image.LANCZOS"),
|
(LANCZOS, "Image.LANCZOS"),
|
||||||
|
@ -2529,7 +2517,7 @@ class Image:
|
||||||
message = f"Unknown resampling filter ({resample})."
|
message = f"Unknown resampling filter ({resample})."
|
||||||
|
|
||||||
filters = [
|
filters = [
|
||||||
"{} ({})".format(filter[1], filter[0])
|
f"{filter[1]} ({filter[0]})"
|
||||||
for filter in (
|
for filter in (
|
||||||
(NEAREST, "Image.NEAREST"),
|
(NEAREST, "Image.NEAREST"),
|
||||||
(BILINEAR, "Image.BILINEAR"),
|
(BILINEAR, "Image.BILINEAR"),
|
||||||
|
|
|
@ -174,13 +174,11 @@ class ImageDraw:
|
||||||
angle -= 90
|
angle -= 90
|
||||||
distance = width / 2 - 1
|
distance = width / 2 - 1
|
||||||
return tuple(
|
return tuple(
|
||||||
[
|
|
||||||
p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d))
|
p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d))
|
||||||
for p, p_d in (
|
for p, p_d in (
|
||||||
(x, distance * math.cos(math.radians(angle))),
|
(x, distance * math.cos(math.radians(angle))),
|
||||||
(y, distance * math.sin(math.radians(angle))),
|
(y, distance * math.sin(math.radians(angle))),
|
||||||
)
|
)
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flipped = (
|
flipped = (
|
||||||
|
@ -979,6 +977,6 @@ def _color_diff(color1, color2):
|
||||||
Uses 1-norm distance to calculate difference between two values.
|
Uses 1-norm distance to calculate difference between two values.
|
||||||
"""
|
"""
|
||||||
if isinstance(color2, tuple):
|
if isinstance(color2, tuple):
|
||||||
return sum([abs(color1[i] - color2[i]) for i in range(0, len(color2))])
|
return sum(abs(color1[i] - color2[i]) for i in range(0, len(color2)))
|
||||||
else:
|
else:
|
||||||
return abs(color1 - color2)
|
return abs(color1 - color2)
|
||||||
|
|
|
@ -425,7 +425,7 @@ class PdfParser:
|
||||||
self.f.write(b"%PDF-1.4\n")
|
self.f.write(b"%PDF-1.4\n")
|
||||||
|
|
||||||
def write_comment(self, s):
|
def write_comment(self, s):
|
||||||
self.f.write(f"% {s}\n".encode("utf-8"))
|
self.f.write(f"% {s}\n".encode())
|
||||||
|
|
||||||
def write_catalog(self):
|
def write_catalog(self):
|
||||||
self.del_root()
|
self.del_root()
|
||||||
|
@ -862,7 +862,7 @@ class PdfParser:
|
||||||
if m:
|
if m:
|
||||||
# filter out whitespace
|
# filter out whitespace
|
||||||
hex_string = bytearray(
|
hex_string = bytearray(
|
||||||
[b for b in m.group(1) if b in b"0123456789abcdefABCDEF"]
|
b for b in m.group(1) if b in b"0123456789abcdefABCDEF"
|
||||||
)
|
)
|
||||||
if len(hex_string) % 2 == 1:
|
if len(hex_string) % 2 == 1:
|
||||||
# append a 0 if the length is not even - yes, at the end
|
# append a 0 if the length is not even - yes, at the end
|
||||||
|
|
|
@ -674,7 +674,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
_load_dispatch[idx] = ( # noqa: F821
|
_load_dispatch[idx] = ( # noqa: F821
|
||||||
size,
|
size,
|
||||||
lambda self, data, legacy_api=True: (
|
lambda self, data, legacy_api=True: (
|
||||||
self._unpack("{}{}".format(len(data) // size, fmt), data)
|
self._unpack(f"{len(data) // size}{fmt}", data)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
_write_dispatch[idx] = lambda self, *values: ( # noqa: F821
|
_write_dispatch[idx] = lambda self, *values: ( # noqa: F821
|
||||||
|
@ -718,7 +718,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
|
|
||||||
@_register_loader(5, 8)
|
@_register_loader(5, 8)
|
||||||
def load_rational(self, data, legacy_api=True):
|
def load_rational(self, data, legacy_api=True):
|
||||||
vals = self._unpack("{}L".format(len(data) // 4), data)
|
vals = self._unpack(f"{len(data) // 4}L", data)
|
||||||
|
|
||||||
def combine(a, b):
|
def combine(a, b):
|
||||||
return (a, b) if legacy_api else IFDRational(a, b)
|
return (a, b) if legacy_api else IFDRational(a, b)
|
||||||
|
@ -741,7 +741,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
|
|
||||||
@_register_loader(10, 8)
|
@_register_loader(10, 8)
|
||||||
def load_signed_rational(self, data, legacy_api=True):
|
def load_signed_rational(self, data, legacy_api=True):
|
||||||
vals = self._unpack("{}l".format(len(data) // 4), data)
|
vals = self._unpack(f"{len(data) // 4}l", data)
|
||||||
|
|
||||||
def combine(a, b):
|
def combine(a, b):
|
||||||
return (a, b) if legacy_api else IFDRational(a, b)
|
return (a, b) if legacy_api else IFDRational(a, b)
|
||||||
|
|
|
@ -9,12 +9,6 @@
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
|
||||||
/* Workaround issue #2479 */
|
|
||||||
#if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) && \
|
|
||||||
!defined(PYPY_VERSION)
|
|
||||||
#undef PySlice_GetIndicesEx
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Check that we have an ANSI compliant compiler */
|
/* Check that we have an ANSI compliant compiler */
|
||||||
#ifndef HAVE_PROTOTYPES
|
#ifndef HAVE_PROTOTYPES
|
||||||
#error Sorry, this library requires support for ANSI prototypes.
|
#error Sorry, this library requires support for ANSI prototypes.
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -6,7 +6,7 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist =
|
envlist =
|
||||||
lint
|
lint
|
||||||
py{36,37,38,39,310,py3}
|
py{37,38,39,310,py3}
|
||||||
minversion = 1.9
|
minversion = 1.9
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
|
|
@ -474,8 +474,6 @@ def build_pillow():
|
||||||
cmd_cd("{pillow_dir}"),
|
cmd_cd("{pillow_dir}"),
|
||||||
*prefs["header"],
|
*prefs["header"],
|
||||||
cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow
|
cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow
|
||||||
cmd_set("MSSdk", "1"), # for PyPy3.6
|
|
||||||
cmd_set("py_vcruntime_redist", "true"), # use /MD, not /MT
|
|
||||||
r'"{python_dir}\{python_exe}" setup.py build_ext --vendor-raqm --vendor-fribidi %*', # noqa: E501
|
r'"{python_dir}\{python_exe}" setup.py build_ext --vendor-raqm --vendor-fribidi %*', # noqa: E501
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user