Skip to content

Commit e6c528d

Browse files
authored
Merge branch 'main' into palette
2 parents 4b5c1e8 + 8246673 commit e6c528d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+446
-267
lines changed

.ci/requirements-mypy.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
mypy==1.9.0
1+
mypy==1.10.0

.github/workflows/test-cygwin.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
packages: >
5656
gcc-g++
5757
ghostscript
58+
git
5859
ImageMagick
5960
jpeg
6061
libfreetype-devel
@@ -132,11 +133,12 @@ jobs:
132133
bash.exe .ci/after_success.sh
133134
134135
- name: Upload coverage
135-
uses: codecov/codecov-action@v3.1.5
136+
uses: codecov/codecov-action@v4
136137
with:
137138
file: ./coverage.xml
138139
flags: GHA_Cygwin
139140
name: Cygwin Python 3.${{ matrix.python-minor-version }}
141+
token: ${{ secrets.CODECOV_ORG_TOKEN }}
140142

141143
success:
142144
permissions:

.github/workflows/test-docker.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ jobs:
3636
docker: [
3737
# Run slower jobs first to give them a headstart and reduce waiting time
3838
ubuntu-22.04-jammy-arm64v8,
39-
ubuntu-22.04-jammy-ppc64le,
40-
ubuntu-22.04-jammy-s390x,
39+
ubuntu-24.04-noble-ppc64le,
40+
ubuntu-24.04-noble-s390x,
4141
# Then run the remainder
4242
alpine,
4343
amazon-2-amd64,
@@ -47,19 +47,20 @@ jobs:
4747
debian-11-bullseye-amd64,
4848
debian-12-bookworm-x86,
4949
debian-12-bookworm-amd64,
50-
fedora-38-amd64,
5150
fedora-39-amd64,
51+
fedora-40-amd64,
5252
gentoo,
5353
ubuntu-20.04-focal-amd64,
5454
ubuntu-22.04-jammy-amd64,
55+
ubuntu-24.04-noble-amd64,
5556
]
5657
dockerTag: [main]
5758
include:
5859
- docker: "ubuntu-22.04-jammy-arm64v8"
5960
qemu-arch: "aarch64"
60-
- docker: "ubuntu-22.04-jammy-ppc64le"
61+
- docker: "ubuntu-24.04-noble-ppc64le"
6162
qemu-arch: "ppc64le"
62-
- docker: "ubuntu-22.04-jammy-s390x"
63+
- docker: "ubuntu-24.04-noble-s390x"
6364
qemu-arch: "s390x"
6465

6566
name: ${{ matrix.docker }}
@@ -81,8 +82,8 @@ jobs:
8182
8283
- name: Docker build
8384
run: |
84-
# The Pillow user in the docker container is UID 1000
85-
sudo chown -R 1000 $GITHUB_WORKSPACE
85+
# The Pillow user in the docker container is UID 1001
86+
sudo chown -R 1001 $GITHUB_WORKSPACE
8687
docker run --name pillow_container -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
8788
sudo chown -R runner $GITHUB_WORKSPACE
8889
@@ -99,11 +100,12 @@ jobs:
99100
MATRIX_DOCKER: ${{ matrix.docker }}
100101

101102
- name: Upload coverage
102-
uses: codecov/codecov-action@v3.1.5
103+
uses: codecov/codecov-action@v4
103104
with:
104105
flags: GHA_Docker
105106
name: ${{ matrix.docker }}
106107
gcov: true
108+
token: ${{ secrets.CODECOV_ORG_TOKEN }}
107109

108110
success:
109111
permissions:

.github/workflows/test-mingw.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ jobs:
8585
python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests
8686
8787
- name: Upload coverage
88-
uses: codecov/codecov-action@v3.1.5
88+
uses: codecov/codecov-action@v4
8989
with:
9090
file: ./coverage.xml
9191
flags: GHA_Windows
9292
name: "MSYS2 MinGW"
93+
token: ${{ secrets.CODECOV_ORG_TOKEN }}

.github/workflows/test-valgrind.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
5151
- name: Build and Run Valgrind
5252
run: |
53-
# The Pillow user in the docker container is UID 1000
54-
sudo chown -R 1000 $GITHUB_WORKSPACE
53+
# The Pillow user in the docker container is UID 1001
54+
sudo chown -R 1001 $GITHUB_WORKSPACE
5555
docker run --name pillow_container -e "PILLOW_VALGRIND_TEST=true" -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
5656
sudo chown -R runner $GITHUB_WORKSPACE

.github/workflows/test-windows.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,12 @@ jobs:
213213
shell: pwsh
214214

215215
- name: Upload coverage
216-
uses: codecov/codecov-action@v3.1.5
216+
uses: codecov/codecov-action@v4
217217
with:
218218
file: ./coverage.xml
219219
flags: GHA_Windows
220220
name: ${{ runner.os }} Python ${{ matrix.python-version }}
221+
token: ${{ secrets.CODECOV_ORG_TOKEN }}
221222

222223
success:
223224
permissions:

.github/workflows/test.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ jobs:
5757
- python-version: "3.10"
5858
PYTHONOPTIMIZE: 2
5959
# M1 only available for 3.10+
60-
- os: "macos-latest"
60+
- os: "macos-13"
6161
python-version: "3.9"
62-
- os: "macos-latest"
62+
- os: "macos-13"
6363
python-version: "3.8"
6464
exclude:
6565
- os: "macos-14"
@@ -150,11 +150,12 @@ jobs:
150150
.ci/after_success.sh
151151
152152
- name: Upload coverage
153-
uses: codecov/codecov-action@v3.1.5
153+
uses: codecov/codecov-action@v4
154154
with:
155155
flags: ${{ matrix.os == 'ubuntu-latest' && 'GHA_Ubuntu' || 'GHA_macOS' }}
156156
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
157157
gcov: true
158+
token: ${{ secrets.CODECOV_ORG_TOKEN }}
158159

159160
success:
160161
permissions:

.github/workflows/wheels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797
matrix:
9898
include:
9999
- name: "macOS x86_64"
100-
os: macos-latest
100+
os: macos-13
101101
cibw_arch: x86_64
102102
macosx_deployment_target: "10.10"
103103
- name: "macOS arm64"

.readthedocs.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ build:
66
os: ubuntu-22.04
77
tools:
88
python: "3"
9+
jobs:
10+
post_checkout:
11+
- git remote add upstream https://github.com/python-pillow/Pillow.git # For forks
12+
- git fetch upstream --tags
913

1014
python:
1115
install:

CHANGES.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@ Changelog (Pillow)
55
10.4.0 (unreleased)
66
-------------------
77

8+
- Deprecate BGR;15, BGR;16 and BGR;24 modes #7978
9+
[radarhere, hugovk]
10+
11+
- Fix ImagingAccess for I;16N on big-endian #7921
12+
[Yay295, radarhere]
13+
14+
- Support reading P mode TIFF images with padding #7996
15+
[radarhere]
16+
17+
- Deprecate support for libtiff < 4 #7998
18+
[radarhere, hugovk]
19+
20+
- Corrected ImageShow UnixViewer command #7987
21+
[radarhere]
22+
23+
- Use functools.cached_property in ImageStat #7952
24+
[nulano, hugovk, radarhere]
25+
826
- Add support for reading BITMAPV2INFOHEADER and BITMAPV3INFOHEADER #7956
927
[Cirras, radarhere]
1028

Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
.PHONY: clean
44
clean:
5-
python3 setup.py clean
65
rm src/PIL/*.so || true
76
rm -r build || true
87
find . -name __pycache__ | xargs rm -r || true

Tests/helper.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,33 @@
2929
uploader = "github_actions"
3030

3131

32+
modes = (
33+
"1",
34+
"L",
35+
"LA",
36+
"La",
37+
"P",
38+
"PA",
39+
"F",
40+
"I",
41+
"I;16",
42+
"I;16L",
43+
"I;16B",
44+
"I;16N",
45+
"RGB",
46+
"RGBA",
47+
"RGBa",
48+
"RGBX",
49+
"BGR;15",
50+
"BGR;16",
51+
"BGR;24",
52+
"CMYK",
53+
"YCbCr",
54+
"HSV",
55+
"LAB",
56+
)
57+
58+
3259
def upload(a: Image.Image, b: Image.Image) -> str | None:
3360
if uploader == "show":
3461
# local img.show for errors.
@@ -273,7 +300,18 @@ def _cached_hopper(mode: str) -> Image.Image:
273300
im = hopper("L")
274301
else:
275302
im = hopper()
276-
return im.convert(mode)
303+
if mode.startswith("BGR;"):
304+
with pytest.warns(DeprecationWarning):
305+
im = im.convert(mode)
306+
else:
307+
try:
308+
im = im.convert(mode)
309+
except ImportError:
310+
if mode == "LAB":
311+
im = Image.open("Tests/images/hopper.Lab.tif")
312+
else:
313+
raise
314+
return im
277315

278316

279317
def djpeg_available() -> bool:

Tests/test_features.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ def test(name: str, function: Callable[[str], bool]) -> None:
3737
else:
3838
assert function(name) == version
3939
if name != "PIL":
40+
if name == "zlib" and version is not None:
41+
version = version.replace(".zlib-ng", "")
4042
assert version is None or re.search(r"\d+(\.\d+)*$", version)
4143

4244
for module in features.modules:

Tests/test_file_libtiff.py

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,24 @@ def test_additional_metadata(
242242

243243
im.save(out, tiffinfo=new_ifd)
244244

245-
def test_custom_metadata(self, tmp_path: Path) -> None:
245+
@pytest.mark.parametrize(
246+
"libtiff",
247+
(
248+
pytest.param(
249+
True,
250+
marks=pytest.mark.skipif(
251+
not getattr(Image.core, "libtiff_support_custom_tags", False),
252+
reason="Custom tags not supported by older libtiff",
253+
),
254+
),
255+
False,
256+
),
257+
)
258+
def test_custom_metadata(
259+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path, libtiff: bool
260+
) -> None:
261+
monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff)
262+
246263
class Tc(NamedTuple):
247264
value: Any
248265
type: int
@@ -281,53 +298,43 @@ class Tc(NamedTuple):
281298
)
282299
}
283300

284-
libtiffs = [False]
285-
if Image.core.libtiff_support_custom_tags:
286-
libtiffs.append(True)
287-
288-
for libtiff in libtiffs:
289-
TiffImagePlugin.WRITE_LIBTIFF = libtiff
290-
291-
def check_tags(
292-
tiffinfo: TiffImagePlugin.ImageFileDirectory_v2 | dict[int, str]
293-
) -> None:
294-
im = hopper()
295-
296-
out = str(tmp_path / "temp.tif")
297-
im.save(out, tiffinfo=tiffinfo)
298-
299-
with Image.open(out) as reloaded:
300-
for tag, value in tiffinfo.items():
301-
reloaded_value = reloaded.tag_v2[tag]
302-
if (
303-
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
304-
and libtiff
305-
):
306-
# libtiff does not support real RATIONALS
307-
assert (
308-
round(abs(float(reloaded_value) - float(value)), 7) == 0
309-
)
310-
continue
311-
312-
assert reloaded_value == value
313-
314-
# Test with types
315-
ifd = TiffImagePlugin.ImageFileDirectory_v2()
316-
for tag, tagdata in custom.items():
317-
ifd[tag] = tagdata.value
318-
ifd.tagtype[tag] = tagdata.type
319-
check_tags(ifd)
320-
321-
# Test without types. This only works for some types, int for example are
322-
# always encoded as LONG and not SIGNED_LONG.
323-
check_tags(
324-
{
325-
tag: tagdata.value
326-
for tag, tagdata in custom.items()
327-
if tagdata.supported_by_default
328-
}
329-
)
330-
TiffImagePlugin.WRITE_LIBTIFF = False
301+
def check_tags(
302+
tiffinfo: TiffImagePlugin.ImageFileDirectory_v2 | dict[int, str]
303+
) -> None:
304+
im = hopper()
305+
306+
out = str(tmp_path / "temp.tif")
307+
im.save(out, tiffinfo=tiffinfo)
308+
309+
with Image.open(out) as reloaded:
310+
for tag, value in tiffinfo.items():
311+
reloaded_value = reloaded.tag_v2[tag]
312+
if (
313+
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
314+
and libtiff
315+
):
316+
# libtiff does not support real RATIONALS
317+
assert round(abs(float(reloaded_value) - float(value)), 7) == 0
318+
continue
319+
320+
assert reloaded_value == value
321+
322+
# Test with types
323+
ifd = TiffImagePlugin.ImageFileDirectory_v2()
324+
for tag, tagdata in custom.items():
325+
ifd[tag] = tagdata.value
326+
ifd.tagtype[tag] = tagdata.type
327+
check_tags(ifd)
328+
329+
# Test without types. This only works for some types, int for example are
330+
# always encoded as LONG and not SIGNED_LONG.
331+
check_tags(
332+
{
333+
tag: tagdata.value
334+
for tag, tagdata in custom.items()
335+
if tagdata.supported_by_default
336+
}
337+
)
331338

332339
def test_osubfiletype(self, tmp_path: Path) -> None:
333340
outfile = str(tmp_path / "temp.tif")
@@ -741,7 +748,7 @@ def test_read_icc(self, monkeypatch: pytest.MonkeyPatch) -> None:
741748
pytest.param(
742749
True,
743750
marks=pytest.mark.skipif(
744-
not Image.core.libtiff_support_custom_tags,
751+
not getattr(Image.core, "libtiff_support_custom_tags", False),
745752
reason="Custom tags not supported by older libtiff",
746753
),
747754
),

Tests/test_file_png.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ def get_chunks(self, filename: str) -> list[bytes]:
8585

8686
def test_sanity(self, tmp_path: Path) -> None:
8787
# internal version number
88-
assert re.search(r"\d+(\.\d+){1,3}$", features.version_codec("zlib"))
88+
assert re.search(
89+
r"\d+(\.\d+){1,3}(\.zlib\-ng)?$", features.version_codec("zlib")
90+
)
8991

9092
test_file = str(tmp_path / "temp.png")
9193

0 commit comments

Comments
 (0)