Skip to content

Commit 8dee8dd

Browse files
authored
Add ImageFile context manager (#9367)
2 parents b2d9bc3 + ce11a0c commit 8dee8dd

6 files changed

Lines changed: 19 additions & 14 deletions

File tree

Tests/test_file_jpeg2k.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def test_reduce() -> None:
164164
with Image.open("Tests/images/test-card-lossless.jp2") as im:
165165
assert callable(im.reduce)
166166

167-
im.reduce = 2
167+
im.reduce = 2 # type: ignore[assignment, method-assign]
168168
assert im.reduce == 2
169169

170170
im.load()

Tests/test_file_png.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ class MyStdOut:
823823
monkeypatch.setattr(sys, "stdout", mystdout)
824824

825825
with Image.open(TEST_PNG_FILE) as im:
826-
im.save(sys.stdout, "PNG")
826+
im.save(sys.stdout, "PNG") # type: ignore[arg-type]
827827

828828
if isinstance(mystdout, MyStdOut):
829829
mystdout = mystdout.buffer

Tests/test_file_ppm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ class MyStdOut:
389389
monkeypatch.setattr(sys, "stdout", mystdout)
390390

391391
with Image.open(TEST_FILE) as im:
392-
im.save(sys.stdout, "PPM")
392+
im.save(sys.stdout, "PPM") # type: ignore[arg-type]
393393

394394
if isinstance(mystdout, MyStdOut):
395395
mystdout = mystdout.buffer

Tests/test_image_transform.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,14 +250,14 @@ def test_blank_fill(self) -> None:
250250
def test_missing_method_data(self) -> None:
251251
with hopper() as im:
252252
with pytest.raises(ValueError):
253-
im.transform((100, 100), None)
253+
im.transform((100, 100), None) # type: ignore[arg-type]
254254

255255
@pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown"))
256256
def test_unknown_resampling_filter(self, resample: Image.Resampling | str) -> None:
257257
with hopper() as im:
258258
(w, h) = im.size
259259
with pytest.raises(ValueError):
260-
im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample)
260+
im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample) # type: ignore[arg-type]
261261

262262

263263
class TestImageTransformAffine:

src/PIL/Image.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -590,16 +590,11 @@ def _new(self, im: core.ImagingCore) -> Image:
590590
return new
591591

592592
# Context manager support
593-
def __enter__(self):
593+
def __enter__(self) -> Image:
594594
return self
595595

596-
def __exit__(self, *args):
597-
from . import ImageFile
598-
599-
if isinstance(self, ImageFile.ImageFile):
600-
if getattr(self, "_exclusive_fp", False):
601-
self._close_fp()
602-
self.fp = None
596+
def __exit__(self, *args: object) -> None:
597+
pass
603598

604599
def close(self) -> None:
605600
"""

src/PIL/ImageFile.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def __init__(
131131
self.decoderconfig: tuple[Any, ...] = ()
132132
self.decodermaxblock = MAXBLOCK
133133

134+
self.fp: IO[bytes] | None
134135
self._fp: IO[bytes] | DeferredError
135136
if is_path(fp):
136137
# filename
@@ -168,6 +169,10 @@ def __init__(
168169
def _open(self) -> None:
169170
pass
170171

172+
# Context manager support
173+
def __enter__(self) -> ImageFile:
174+
return self
175+
171176
def _close_fp(self) -> None:
172177
if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError):
173178
if self._fp != self.fp:
@@ -176,6 +181,11 @@ def _close_fp(self) -> None:
176181
if self.fp:
177182
self.fp.close()
178183

184+
def __exit__(self, *args: object) -> None:
185+
if getattr(self, "_exclusive_fp", False):
186+
self._close_fp()
187+
self.fp = None
188+
179189
def close(self) -> None:
180190
"""
181191
Closes the file pointer, if possible.
@@ -268,7 +278,7 @@ def verify(self) -> None:
268278

269279
# raise exception if something's wrong. must be called
270280
# directly after open, and closes file when finished.
271-
if self._exclusive_fp:
281+
if self._exclusive_fp and self.fp:
272282
self.fp.close()
273283
self.fp = None
274284

0 commit comments

Comments
 (0)