Skip to content

Commit 29ff5fc

Browse files
authored
Use monkeypatch (python-pillow#9406)
Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
1 parent 6a5c588 commit 29ff5fc

File tree

8 files changed

+69
-84
lines changed

8 files changed

+69
-84
lines changed

Tests/test_file_pcx.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -119,36 +119,36 @@ def test_large_count(tmp_path: Path) -> None:
119119
_roundtrip(tmp_path, im)
120120

121121

122-
def _test_buffer_overflow(tmp_path: Path, im: Image.Image, size: int = 1024) -> None:
123-
_last = ImageFile.MAXBLOCK
124-
ImageFile.MAXBLOCK = size
125-
try:
126-
_roundtrip(tmp_path, im)
127-
finally:
128-
ImageFile.MAXBLOCK = _last
122+
def _test_buffer_overflow(
123+
tmp_path: Path, im: Image.Image, monkeypatch: pytest.MonkeyPatch
124+
) -> None:
125+
monkeypatch.setattr(ImageFile, "MAXBLOCK", 1024)
126+
_roundtrip(tmp_path, im)
129127

130128

131-
def test_break_in_count_overflow(tmp_path: Path) -> None:
129+
def test_break_in_count_overflow(
130+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
131+
) -> None:
132132
im = Image.new("L", (256, 5))
133133
px = im.load()
134134
assert px is not None
135135
for y in range(4):
136136
for x in range(256):
137137
px[x, y] = x % 128
138-
_test_buffer_overflow(tmp_path, im)
138+
_test_buffer_overflow(tmp_path, im, monkeypatch)
139139

140140

141-
def test_break_one_in_loop(tmp_path: Path) -> None:
141+
def test_break_one_in_loop(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
142142
im = Image.new("L", (256, 5))
143143
px = im.load()
144144
assert px is not None
145145
for y in range(5):
146146
for x in range(256):
147147
px[x, y] = x % 128
148-
_test_buffer_overflow(tmp_path, im)
148+
_test_buffer_overflow(tmp_path, im, monkeypatch)
149149

150150

151-
def test_break_many_in_loop(tmp_path: Path) -> None:
151+
def test_break_many_in_loop(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
152152
im = Image.new("L", (256, 5))
153153
px = im.load()
154154
assert px is not None
@@ -157,21 +157,21 @@ def test_break_many_in_loop(tmp_path: Path) -> None:
157157
px[x, y] = x % 128
158158
for x in range(8):
159159
px[x, 4] = 16
160-
_test_buffer_overflow(tmp_path, im)
160+
_test_buffer_overflow(tmp_path, im, monkeypatch)
161161

162162

163-
def test_break_one_at_end(tmp_path: Path) -> None:
163+
def test_break_one_at_end(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
164164
im = Image.new("L", (256, 5))
165165
px = im.load()
166166
assert px is not None
167167
for y in range(5):
168168
for x in range(256):
169169
px[x, y] = x % 128
170170
px[0, 3] = 128 + 64
171-
_test_buffer_overflow(tmp_path, im)
171+
_test_buffer_overflow(tmp_path, im, monkeypatch)
172172

173173

174-
def test_break_many_at_end(tmp_path: Path) -> None:
174+
def test_break_many_at_end(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
175175
im = Image.new("L", (256, 5))
176176
px = im.load()
177177
assert px is not None
@@ -181,10 +181,10 @@ def test_break_many_at_end(tmp_path: Path) -> None:
181181
for x in range(4):
182182
px[x * 2, 3] = 128 + 64
183183
px[x + 256 - 4, 3] = 0
184-
_test_buffer_overflow(tmp_path, im)
184+
_test_buffer_overflow(tmp_path, im, monkeypatch)
185185

186186

187-
def test_break_padding(tmp_path: Path) -> None:
187+
def test_break_padding(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
188188
im = Image.new("L", (257, 5))
189189
px = im.load()
190190
assert px is not None
@@ -193,4 +193,4 @@ def test_break_padding(tmp_path: Path) -> None:
193193
px[x, y] = x % 128
194194
for x in range(5):
195195
px[x, 3] = 0
196-
_test_buffer_overflow(tmp_path, im)
196+
_test_buffer_overflow(tmp_path, im, monkeypatch)

Tests/test_file_png.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -654,21 +654,17 @@ def test_unknown_compression_method(self) -> None:
654654
with pytest.raises(SyntaxError, match="Unknown compression method"):
655655
PngImagePlugin.PngImageFile("Tests/images/unknown_compression_method.png")
656656

657-
def test_padded_idat(self) -> None:
657+
def test_padded_idat(self, monkeypatch: pytest.MonkeyPatch) -> None:
658658
# This image has been manually hexedited
659659
# so that the IDAT chunk has padding at the end
660660
# Set MAXBLOCK to the length of the actual data
661661
# so that the decoder finishes reading before the chunk ends
662-
MAXBLOCK = ImageFile.MAXBLOCK
663-
ImageFile.MAXBLOCK = 45
664-
ImageFile.LOAD_TRUNCATED_IMAGES = True
662+
monkeypatch.setattr(ImageFile, "MAXBLOCK", 45)
663+
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
665664

666665
with Image.open("Tests/images/padded_idat.png") as im:
667666
im.load()
668667

669-
ImageFile.MAXBLOCK = MAXBLOCK
670-
ImageFile.LOAD_TRUNCATED_IMAGES = False
671-
672668
assert_image_equal_tofile(im, "Tests/images/bw_gradient.png")
673669

674670
@pytest.mark.parametrize(

Tests/test_font_leaks.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
from __future__ import annotations
22

3+
import pytest
4+
35
from PIL import Image, ImageDraw, ImageFont, _util
46

57
from .helper import PillowLeakTestCase, features, skip_unless_feature
68

79
original_core = ImageFont.core
810

911

10-
class TestTTypeFontLeak(PillowLeakTestCase):
11-
# fails at iteration 3 in main
12-
iterations = 10
13-
mem_limit = 4096 # k
14-
12+
class TestFontLeak(PillowLeakTestCase):
1513
def _test_font(self, font: ImageFont.FreeTypeFont | ImageFont.ImageFont) -> None:
1614
im = Image.new("RGB", (255, 255), "white")
1715
draw = ImageDraw.ImageDraw(im)
@@ -21,23 +19,29 @@ def _test_font(self, font: ImageFont.FreeTypeFont | ImageFont.ImageFont) -> None
2119
)
2220
)
2321

22+
23+
class TestTTypeFontLeak(TestFontLeak):
24+
# fails at iteration 3 in main
25+
iterations = 10
26+
mem_limit = 4096 # k
27+
2428
@skip_unless_feature("freetype2")
2529
def test_leak(self) -> None:
2630
ttype = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20)
2731
self._test_font(ttype)
2832

2933

30-
class TestDefaultFontLeak(TestTTypeFontLeak):
34+
class TestDefaultFontLeak(TestFontLeak):
3135
# fails at iteration 37 in main
3236
iterations = 100
3337
mem_limit = 1024 # k
3438

35-
def test_leak(self) -> None:
39+
def test_leak(self, monkeypatch: pytest.MonkeyPatch) -> None:
3640
if features.check_module("freetype2"):
37-
ImageFont.core = _util.DeferredError(ImportError("Disabled for testing"))
38-
try:
39-
default_font = ImageFont.load_default()
40-
finally:
41-
ImageFont.core = original_core
42-
41+
monkeypatch.setattr(
42+
ImageFont,
43+
"core",
44+
_util.DeferredError(ImportError("Disabled for testing")),
45+
)
46+
default_font = ImageFont.load_default()
4347
self._test_font(default_font)

Tests/test_image.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,11 @@ def test_register_open_duplicates(self) -> None:
456456
# Assert
457457
assert len(Image.ID) == id_length
458458

459-
def test_registered_extensions_uninitialized(self) -> None:
459+
def test_registered_extensions_uninitialized(
460+
self, monkeypatch: pytest.MonkeyPatch
461+
) -> None:
460462
# Arrange
461-
Image._initialized = 0
463+
monkeypatch.setattr(Image, "_initialized", 0)
462464

463465
# Act
464466
Image.registered_extensions()

Tests/test_imagedraw.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,21 +1485,15 @@ def test_stroke_multiline() -> None:
14851485

14861486

14871487
@skip_unless_feature("freetype2")
1488-
def test_setting_default_font() -> None:
1489-
# Arrange
1488+
def test_setting_default_font(monkeypatch: pytest.MonkeyPatch) -> None:
14901489
im = Image.new("RGB", (100, 250))
14911490
draw = ImageDraw.Draw(im)
1492-
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
1493-
1494-
# Act
1495-
ImageDraw.ImageDraw.font = font
1491+
assert isinstance(draw.getfont(), ImageFont.load_default().__class__)
14961492

1497-
# Assert
1498-
try:
1499-
assert draw.getfont() == font
1500-
finally:
1501-
ImageDraw.ImageDraw.font = None
1502-
assert isinstance(draw.getfont(), ImageFont.load_default().__class__)
1493+
draw = ImageDraw.Draw(im)
1494+
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
1495+
monkeypatch.setattr(ImageDraw.ImageDraw, "font", font)
1496+
assert draw.getfont() == font
15031497

15041498

15051499
def test_default_font_size() -> None:

Tests/test_imagefile.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232

3333
class TestImageFile:
34-
def test_parser(self) -> None:
34+
def test_parser(self, monkeypatch: pytest.MonkeyPatch) -> None:
3535
def roundtrip(format: str) -> tuple[Image.Image, Image.Image]:
3636
im = hopper("L").resize((1000, 1000), Image.Resampling.NEAREST)
3737
if format in ("MSP", "XBM"):
@@ -55,12 +55,9 @@ def roundtrip(format: str) -> tuple[Image.Image, Image.Image]:
5555
assert_image_equal(*roundtrip("IM"))
5656
assert_image_equal(*roundtrip("MSP"))
5757
if features.check("zlib"):
58-
try:
59-
# force multiple blocks in PNG driver
60-
ImageFile.MAXBLOCK = 8192
61-
assert_image_equal(*roundtrip("PNG"))
62-
finally:
63-
ImageFile.MAXBLOCK = MAXBLOCK
58+
# force multiple blocks in PNG driver
59+
monkeypatch.setattr(ImageFile, "MAXBLOCK", 8192)
60+
assert_image_equal(*roundtrip("PNG"))
6461
assert_image_equal(*roundtrip("PPM"))
6562
assert_image_equal(*roundtrip("TIFF"))
6663
assert_image_equal(*roundtrip("XBM"))
@@ -120,14 +117,11 @@ def test_incremental_webp(self) -> None:
120117
assert (128, 128) == p.image.size
121118

122119
@skip_unless_feature("zlib")
123-
def test_safeblock(self) -> None:
120+
def test_safeblock(self, monkeypatch: pytest.MonkeyPatch) -> None:
124121
im1 = hopper()
125122

126-
try:
127-
ImageFile.SAFEBLOCK = 1
128-
im2 = fromstring(tostring(im1, "PNG"))
129-
finally:
130-
ImageFile.SAFEBLOCK = SAFEBLOCK
123+
monkeypatch.setattr(ImageFile, "SAFEBLOCK", 1)
124+
im2 = fromstring(tostring(im1, "PNG"))
131125

132126
assert_image_equal(im1, im2)
133127

Tests/test_imagefontpil.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,18 @@ def test_invalid_mode() -> None:
3838
font._load_pilfont_data(fp, im)
3939

4040

41-
def test_without_freetype() -> None:
42-
original_core = ImageFont.core
41+
def test_without_freetype(monkeypatch: pytest.MonkeyPatch) -> None:
4342
if features.check_module("freetype2"):
44-
ImageFont.core = _util.DeferredError(ImportError("Disabled for testing"))
45-
try:
46-
with pytest.raises(ImportError):
47-
ImageFont.truetype("Tests/fonts/FreeMono.ttf")
43+
monkeypatch.setattr(
44+
ImageFont, "core", _util.DeferredError(ImportError("Disabled for testing"))
45+
)
46+
with pytest.raises(ImportError):
47+
ImageFont.truetype("Tests/fonts/FreeMono.ttf")
4848

49-
assert isinstance(ImageFont.load_default(), ImageFont.ImageFont)
49+
assert isinstance(ImageFont.load_default(), ImageFont.ImageFont)
5050

51-
with pytest.raises(ImportError):
52-
ImageFont.load_default(size=14)
53-
finally:
54-
ImageFont.core = original_core
51+
with pytest.raises(ImportError):
52+
ImageFont.load_default(size=14)
5553

5654

5755
@pytest.mark.parametrize("font", fonts)

Tests/test_psdraw.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from io import BytesIO
66
from pathlib import Path
77

8+
import pytest
9+
810
from PIL import Image, PSDraw
911

1012

@@ -47,21 +49,16 @@ def test_draw_postscript(tmp_path: Path) -> None:
4749
assert os.path.getsize(tempfile) > 0
4850

4951

50-
def test_stdout() -> None:
52+
def test_stdout(monkeypatch: pytest.MonkeyPatch) -> None:
5153
# Temporarily redirect stdout
52-
old_stdout = sys.stdout
53-
5454
class MyStdOut:
5555
buffer = BytesIO()
5656

5757
mystdout = MyStdOut()
5858

59-
sys.stdout = mystdout
59+
monkeypatch.setattr(sys, "stdout", mystdout)
6060

6161
ps = PSDraw.PSDraw()
6262
_create_document(ps)
6363

64-
# Reset stdout
65-
sys.stdout = old_stdout
66-
6764
assert mystdout.buffer.getvalue() != b""

0 commit comments

Comments
 (0)