Skip to content

Commit

Permalink
Merge pull request #5768 from hugovk/rm-3.6
Browse files Browse the repository at this point in the history
Drop support for soon-EOL Python 3.6
  • Loading branch information
radarhere committed Nov 5, 2021
2 parents fcb87ec + 669ba57 commit a70e3c8
Show file tree
Hide file tree
Showing 20 changed files with 90 additions and 112 deletions.
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ environment:
- PYTHON: C:/Python310
ARCHITECTURE: x86
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- PYTHON: C:/Python36-x64
- PYTHON: C:/Python37-x64
ARCHITECTURE: x64
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ repos:
rev: 911470a610e47d9da5ea938b0887c3df62819b85 # frozen: 21.9b0
hooks:
- id: black
args: ["--target-version", "py36"]
args: ["--target-version", "py37"]
# Only .py files, until https://github.com/psf/black/issues/402 resolved
files: \.py$
types: []
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,5 @@ lint:

.PHONY: lint-fix
lint-fix:
black --target-version py36 .
black --target-version py37 .
isort .
4 changes: 2 additions & 2 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ def test_palette_save_P(tmp_path):
# Forcing a non-straight grayscale palette.

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")
im.save(out, palette=palette)
Expand Down Expand Up @@ -885,7 +885,7 @@ def test_getdata():
im.putpalette(ImagePalette.ImagePalette("RGB"))
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
try:
Expand Down
12 changes: 6 additions & 6 deletions Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,26 +85,26 @@ def test_cmyk(self):
f = "Tests/images/pil_sample_cmyk.jpg"
with Image.open(f) as im:
# 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 m > 0.8
assert y > 0.8
assert k == 0.0
# 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))
]
)
assert k > 0.9
# roundtrip, and check again
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 m > 0.8
assert y > 0.8
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))
]
)
assert k > 0.9

@pytest.mark.parametrize(
Expand Down
4 changes: 1 addition & 3 deletions Tests/test_file_webp.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,7 @@ def test_background_from_gif(self, tmp_path):

with Image.open(out_gif) as reread:
reread_value = reread.convert("RGB").getpixel((1, 1))
difference = sum(
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
)
difference = sum(abs(original_value[i] - reread_value[i]) for i in range(0, 3))
assert difference < 5

@skip_unless_feature("webp")
Expand Down
4 changes: 0 additions & 4 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import io
import os
import shutil
import sys
import tempfile

import pytest
Expand Down Expand Up @@ -782,9 +781,6 @@ def test_exif_load_from_fp(self):
34665: 196,
}

@pytest.mark.skipif(
sys.version_info < (3, 7), reason="Python 3.7 or greater required"
)
def test_categories_deprecation(self):
with pytest.warns(DeprecationWarning):
assert hopper().category == 0
Expand Down
8 changes: 4 additions & 4 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ def test_cbdt(self):
d.text((10, 10), "\U0001f469", font=font, embedded_color=True)

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")
pytest.skip("freetype compiled without libpng or CBDT support")

Expand All @@ -920,7 +920,7 @@ def test_cbdt_mask(self):
assert_image_similar_tofile(
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")
pytest.skip("freetype compiled without libpng or CBDT support")

Expand All @@ -938,7 +938,7 @@ def test_sbix(self):
d.text((50, 50), "\uE901", font=font, embedded_color=True)

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")
pytest.skip("freetype compiled without libpng or SBIX support")

Expand All @@ -956,7 +956,7 @@ def test_sbix_mask(self):
d.text((50, 50), "\uE901", (100, 0, 0), font=font)

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")
pytest.skip("freetype compiled without libpng or SBIX support")

Expand Down
2 changes: 1 addition & 1 deletion Tests/test_imagemath.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

def pixel(im):
if hasattr(im, "im"):
return "{} {}".format(im.mode, repr(im.getpixel((0, 0))))
return f"{im.mode} {repr(im.getpixel((0, 0)))}"
else:
if isinstance(im, int):
return int(im) # hack to deal with booleans
Expand Down
76 changes: 39 additions & 37 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
+======================+=====+=====+=====+=====+=====+=====+=====+=====+
| 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 | | | |
+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+
Expand Down Expand Up @@ -443,42 +445,42 @@ Continuous Integration Targets

These platforms are built and tested for every change.

+----------------------------------+---------------------------------+---------------------+
| Operating system | Tested Python versions | Tested architecture |
+==================================+=================================+=====================+
| Alpine | 3.9 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| Amazon Linux 2 | 3.7 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| Arch | 3.9 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| CentOS 7 | 3.6 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| CentOS 8 | 3.6 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| CentOS Stream 8 | 3.6 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| Debian 10 Buster | 3.7 | x86 |
+----------------------------------+---------------------------------+---------------------+
| Fedora 34 | 3.9 | 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 |
+----------------------------------+---------------------------------+---------------------+
| Ubuntu Linux 18.04 LTS (Bionic) | 3.6 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| Ubuntu Linux 20.04 LTS (Focal) | 3.6, 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 |
| +---------------------------------+---------------------+
| | 3.8 | arm64v8, ppc64le, |
| | | s390x |
+----------------------------------+---------------------------------+---------------------+
| Windows Server 2016 | 3.6 | x86-64 |
+----------------------------------+---------------------------------+---------------------+
| Windows Server 2019 | 3.6, 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 |
| +---------------------------------+---------------------+
| | 3.9/MinGW | x86, x86-64 |
+----------------------------------+---------------------------------+---------------------+
+----------------------------------+----------------------------+---------------------+
| Operating system | Tested Python versions | Tested architecture |
+==================================+============================+=====================+
| Alpine | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Amazon Linux 2 | 3.7 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Arch | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS 7 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS 8 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS Stream 8 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Debian 10 Buster | 3.7 | x86 |
+----------------------------------+----------------------------+---------------------+
| Fedora 34 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Fedora 35 | 3.10 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| macOS 10.15 Catalina | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Ubuntu Linux 18.04 LTS (Bionic) | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Ubuntu Linux 20.04 LTS (Focal) | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 |
| +----------------------------+---------------------+
| | 3.8 | arm64v8, ppc64le, |
| | | s390x |
+----------------------------------+----------------------------+---------------------+
| Windows Server 2016 | 3.7 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 |
| +----------------------------+---------------------+
| | 3.9/MinGW | x86, x86-64 |
+----------------------------------+----------------------------+---------------------+


Other Platforms
Expand Down
5 changes: 5 additions & 0 deletions docs/releasenotes/9.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
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
^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
3 changes: 1 addition & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ classifiers =
License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Expand All @@ -34,7 +33,7 @@ project_urls =
Twitter=https://twitter.com/PythonPillow

[options]
python_requires = >=3.6
python_requires = >=3.7

[flake8]
extend-ignore = E203
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/GifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def load_prepare(self):
if not self.im and "transparency" in self.info:
self.im = Image.core.fill(self.mode, self.size, self.info["transparency"])

super(GifImageFile, self).load_prepare()
super().load_prepare()

def tell(self):
return self.__frame
Expand Down
42 changes: 15 additions & 27 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,26 +51,18 @@
from ._binary import i32le
from ._util import deferred_error, isPath

if sys.version_info >= (3, 7):

def __getattr__(name):
categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2}
if name in categories:
warnings.warn(
"Image categories are deprecated and will be removed in Pillow 10 "
"(2023-07-01). Use is_animated instead.",
DeprecationWarning,
stacklevel=2,
)
return categories[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


else:
# categories
NORMAL = 0
SEQUENCE = 1
CONTAINER = 2
def __getattr__(name):
categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2}
if name in categories:
warnings.warn(
"Image categories are deprecated and will be removed in Pillow 10 "
"(2023-07-01). Use is_animated instead.",
DeprecationWarning,
stacklevel=2,
)
return categories[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -926,12 +918,8 @@ def convert_transparency(m, v):
transparency = convert_transparency(matrix, transparency)
elif len(mode) == 3:
transparency = tuple(
[
convert_transparency(
matrix[i * 4 : i * 4 + 4], transparency
)
for i in range(0, len(transparency))
]
convert_transparency(matrix[i * 4 : i * 4 + 4], transparency)
for i in range(0, len(transparency))
)
new.info["transparency"] = transparency
return new
Expand Down Expand Up @@ -1934,7 +1922,7 @@ def resize(self, size, resample=None, box=None, reducing_gap=None):
message = f"Unknown resampling filter ({resample})."

filters = [
"{} ({})".format(filter[1], filter[0])
f"{filter[1]} ({filter[0]})"
for filter in (
(NEAREST, "Image.NEAREST"),
(LANCZOS, "Image.LANCZOS"),
Expand Down Expand Up @@ -2529,7 +2517,7 @@ def __transformer(self, box, image, method, data, resample=NEAREST, fill=1):
message = f"Unknown resampling filter ({resample})."

filters = [
"{} ({})".format(filter[1], filter[0])
f"{filter[1]} ({filter[0]})"
for filter in (
(NEAREST, "Image.NEAREST"),
(BILINEAR, "Image.BILINEAR"),
Expand Down
14 changes: 6 additions & 8 deletions src/PIL/ImageDraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,11 @@ def coord_at_angle(coord, angle):
angle -= 90
distance = width / 2 - 1
return tuple(
[
p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d))
for p, p_d in (
(x, distance * math.cos(math.radians(angle))),
(y, distance * math.sin(math.radians(angle))),
)
]
p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d))
for p, p_d in (
(x, distance * math.cos(math.radians(angle))),
(y, distance * math.sin(math.radians(angle))),
)
)

flipped = (
Expand Down Expand Up @@ -979,6 +977,6 @@ def _color_diff(color1, color2):
Uses 1-norm distance to calculate difference between two values.
"""
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:
return abs(color1 - color2)
4 changes: 2 additions & 2 deletions src/PIL/PdfParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ def write_header(self):
self.f.write(b"%PDF-1.4\n")

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):
self.del_root()
Expand Down Expand Up @@ -862,7 +862,7 @@ def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1):
if m:
# filter out whitespace
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:
# append a 0 if the length is not even - yes, at the end
Expand Down

0 comments on commit a70e3c8

Please sign in to comment.