Skip to content

Commit 7c1736d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into pyest-xdist
2 parents e87b14d + 892fd2c commit 7c1736d

33 files changed

+385
-98
lines changed

.ci/install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ if [[ $(uname) != CYGWIN* ]]; then
6666
pushd depends && ./install_raqm.sh && popd
6767

6868
# libavif
69-
pushd depends && CMAKE_POLICY_VERSION_MINIMUM=3.5 ./install_libavif.sh && popd
69+
pushd depends && ./install_libavif.sh && popd
7070

7171
# extra test images
7272
pushd depends && ./install_extra_test_images.sh && popd

.ci/requirements-mypy.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ IceSpringPySideStubs-PySide6
44
ipython
55
numpy
66
packaging
7+
pyarrow-stubs
78
pytest
89
sphinx
910
types-atheris
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Test Valgrind Memory Leaks
2+
3+
# like the Docker tests, but running valgrind only on *.c/*.h changes.
4+
5+
# this is very expensive. Only run on the pull request.
6+
on:
7+
# push:
8+
# branches:
9+
# - "**"
10+
# paths:
11+
# - ".github/workflows/test-valgrind.yml"
12+
# - "**.c"
13+
# - "**.h"
14+
pull_request:
15+
paths:
16+
- ".github/workflows/test-valgrind.yml"
17+
- "**.c"
18+
- "**.h"
19+
- "depends/docker-test-valgrind-memory.sh"
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: read
24+
25+
concurrency:
26+
group: ${{ github.workflow }}-${{ github.ref }}
27+
cancel-in-progress: true
28+
29+
jobs:
30+
build:
31+
32+
runs-on: ubuntu-latest
33+
strategy:
34+
fail-fast: false
35+
matrix:
36+
docker: [
37+
ubuntu-22.04-jammy-amd64-valgrind,
38+
]
39+
dockerTag: [main]
40+
41+
name: ${{ matrix.docker }}
42+
43+
steps:
44+
- uses: actions/checkout@v4
45+
with:
46+
persist-credentials: false
47+
48+
- name: Build system information
49+
run: python3 .github/workflows/system-info.py
50+
51+
- name: Docker pull
52+
run: |
53+
docker pull pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
54+
55+
- name: Build and Run Valgrind
56+
run: |
57+
# The Pillow user in the docker container is UID 1001
58+
sudo chown -R 1001 $GITHUB_WORKSPACE
59+
docker run --name pillow_container -e "PILLOW_VALGRIND_TEST=true" -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }} /Pillow/depends/docker-test-valgrind-memory.sh
60+
sudo chown -R runner $GITHUB_WORKSPACE

.github/workflows/test-windows.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,15 @@ env:
3131

3232
jobs:
3333
build:
34-
runs-on: ${{ matrix.os }}
34+
runs-on: windows-latest
3535
strategy:
3636
fail-fast: false
3737
matrix:
3838
python-version: ["pypy3.11", "pypy3.10", "3.10", "3.11", "3.12", "3.13", "3.14"]
3939
architecture: ["x64"]
40-
os: ["windows-latest"]
4140
include:
4241
# Test the oldest Python on 32-bit
43-
- { python-version: "3.9", architecture: "x86", os: "windows-2019" }
42+
- { python-version: "3.9", architecture: "x86" }
4443

4544
timeout-minutes: 45
4645

Makefile

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,27 @@ test:
9797
python3 -c "import pytest" > /dev/null 2>&1 || python3 -m pip install pytest
9898
python3 -m pytest -qq
9999

100+
.PHONY: test-p
101+
test-p:
102+
python3 -c "import xdist" > /dev/null 2>&1 || python3 -m pip install pytest-xdist
103+
python3 -m pytest -qq -n auto
104+
105+
100106
.PHONY: valgrind
101107
valgrind:
102108
python3 -c "import pytest_valgrind" > /dev/null 2>&1 || python3 -m pip install pytest-valgrind
103-
PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp --leak-check=no \
109+
PILLOW_VALGRIND_TEST=true PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp --leak-check=no \
104110
--log-file=/tmp/valgrind-output \
105111
python3 -m pytest --no-memcheck -vv --valgrind --valgrind-log=/tmp/valgrind-output
106112

113+
.PHONY: valgrind-leak
114+
valgrind-leak:
115+
python3 -c "import pytest_valgrind" > /dev/null 2>&1 || python3 -m pip install pytest-valgrind
116+
PILLOW_VALGRIND_TEST=true PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp \
117+
--leak-check=full --show-leak-kinds=definite --errors-for-leak-kinds=definite \
118+
--log-file=/tmp/valgrind-output \
119+
python3 -m pytest -vv --valgrind --valgrind-log=/tmp/valgrind-output
120+
107121
.PHONY: readme
108122
readme:
109123
python3 -c "import markdown2" > /dev/null 2>&1 || python3 -m pip install markdown2

Tests/helper.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ def assert_tuple_approx_equal(
161161
pytest.fail(msg + ": " + repr(actuals) + " != " + repr(targets))
162162

163163

164+
def timeout_unless_slower_valgrind(timeout: float) -> pytest.MarkDecorator:
165+
if "PILLOW_VALGRIND_TEST" in os.environ:
166+
return pytest.mark.pil_noop_mark()
167+
return pytest.mark.timeout(timeout)
168+
169+
164170
def skip_unless_feature(feature: str) -> pytest.MarkDecorator:
165171
reason = f"{feature} not available"
166172
return pytest.mark.skipif(not features.check(feature), reason=reason)

Tests/oss-fuzz/python.supp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,23 @@
1414
fun:_TIFFReadEncodedTileAndAllocBuffer
1515
...
1616
}
17+
18+
{
19+
<python_alloc_possible_leak>
20+
Memcheck:Leak
21+
match-leak-kinds: all
22+
fun:malloc
23+
fun:_PyMem_RawMalloc
24+
fun:PyObject_Malloc
25+
...
26+
}
27+
28+
{
29+
<python_realloc_possible_leak>
30+
Memcheck:Leak
31+
match-leak-kinds: all
32+
fun:malloc
33+
fun:_PyMem_RawRealloc
34+
fun:PyMem_Realloc
35+
...
36+
}

Tests/test_file_eps.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
is_win32,
1616
mark_if_feature_version,
1717
skip_unless_feature,
18+
timeout_unless_slower_valgrind,
1819
)
1920

2021
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
@@ -398,7 +399,7 @@ def test_emptyline() -> None:
398399
assert image.format == "EPS"
399400

400401

401-
@pytest.mark.timeout(timeout=5)
402+
@timeout_unless_slower_valgrind(5)
402403
@pytest.mark.parametrize(
403404
"test_file",
404405
["Tests/images/eps/timeout-d675703545fee17acab56e5fec644c19979175de.eps"],

Tests/test_file_fli.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77

88
from PIL import FliImagePlugin, Image, ImageFile
99

10-
from .helper import assert_image_equal, assert_image_equal_tofile, is_pypy
10+
from .helper import (
11+
assert_image_equal,
12+
assert_image_equal_tofile,
13+
is_pypy,
14+
timeout_unless_slower_valgrind,
15+
)
1116

1217
# created as an export of a palette image from Gimp2.6
1318
# save as...-> hopper.fli, default options.
@@ -189,7 +194,7 @@ def test_seek() -> None:
189194
"Tests/images/timeout-bff0a9dc7243a8e6ede2408d2ffa6a9964698b87.fli",
190195
],
191196
)
192-
@pytest.mark.timeout(timeout=3)
197+
@timeout_unless_slower_valgrind(3)
193198
def test_timeouts(test_file: str) -> None:
194199
with open(test_file, "rb") as f:
195200
with Image.open(f) as im:

Tests/test_file_jpeg.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
is_win32,
3333
mark_if_feature_version,
3434
skip_unless_feature,
35+
timeout_unless_slower_valgrind,
3536
)
3637

3738
ElementTree: ModuleType | None
@@ -1033,7 +1034,7 @@ def test_save_xmp(self, tmp_path: Path) -> None:
10331034
with pytest.raises(ValueError):
10341035
im.save(f, xmp=b"1" * 65505)
10351036

1036-
@pytest.mark.timeout(timeout=1)
1037+
@timeout_unless_slower_valgrind(1)
10371038
def test_eof(self, monkeypatch: pytest.MonkeyPatch) -> None:
10381039
# Even though this decoder never says that it is finished
10391040
# the image should still end when there is no new data

0 commit comments

Comments
 (0)