Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Tests/test_file_bufrstub.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def open(self, im: ImageFile.StubImageFile) -> None:

def load(self, im: ImageFile.StubImageFile) -> Image.Image:
self.loaded = True
assert im.fp is not None
im.fp.close()
return Image.new("RGB", (1, 1))

Expand Down
1 change: 1 addition & 0 deletions Tests/test_file_gribstub.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def open(self, im: Image.Image) -> None:

def load(self, im: ImageFile.ImageFile) -> Image.Image:
self.loaded = True
assert im.fp is not None
im.fp.close()
return Image.new("RGB", (1, 1))

Expand Down
1 change: 1 addition & 0 deletions Tests/test_file_hdf5stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def open(self, im: Image.Image) -> None:

def load(self, im: ImageFile.ImageFile) -> Image.Image:
self.loaded = True
assert im.fp is not None
im.fp.close()
return Image.new("RGB", (1, 1))

Expand Down
3 changes: 2 additions & 1 deletion Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -1133,8 +1133,9 @@ def test_fd_leak(self, tmp_path: Path) -> None:
im.save(tmpfile)

im = Image.open(tmpfile)
assert im.fp is not None
assert not im.fp.closed
fp = im.fp
assert not fp.closed
with pytest.raises(OSError):
os.remove(tmpfile)
im.load()
Expand Down
3 changes: 2 additions & 1 deletion Tests/test_file_libtiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,8 +610,9 @@ def test_bw_compression_w_rgb(self, compression: str, tmp_path: Path) -> None:
im.save(out, compression=compression)

def test_fp_leak(self) -> None:
im: Image.Image | None = Image.open("Tests/images/hopper_g4_500.tif")
im: ImageFile.ImageFile | None = Image.open("Tests/images/hopper_g4_500.tif")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image.open returns an ImageFile instance.

Pillow/src/PIL/Image.py

Lines 3404 to 3408 in 0e3f51d

def open(
fp: StrOrBytesPath | IO[bytes],
mode: Literal["r"] = "r",
formats: list[str] | tuple[str, ...] | None = None,
) -> ImageFile.ImageFile:

assert im is not None
assert im.fp is not None
fn = im.fp.fileno()

os.fstat(fn)
Expand Down
5 changes: 4 additions & 1 deletion Tests/test_file_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,7 @@ def test_close_on_load_exclusive(self, tmp_path: Path) -> None:

im = Image.open(tmpfile)
fp = im.fp
assert fp is not None
assert not fp.closed
im.load()
assert fp.closed
Expand All @@ -984,6 +985,7 @@ def test_close_on_load_nonexclusive(self, tmp_path: Path) -> None:
with open(tmpfile, "rb") as f:
im = Image.open(f)
fp = im.fp
assert fp is not None
assert not fp.closed
im.load()
assert not fp.closed
Expand Down Expand Up @@ -1034,8 +1036,9 @@ def test_fd_leak(self, tmp_path: Path) -> None:
im.save(tmpfile)

im = Image.open(tmpfile)
assert im.fp is not None
assert not im.fp.closed
fp = im.fp
assert not fp.closed
with pytest.raises(OSError):
os.remove(tmpfile)
im.load()
Expand Down
1 change: 1 addition & 0 deletions Tests/test_image_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def test_close_after_load(caplog: pytest.LogCaptureFixture) -> None:
def test_contextmanager() -> None:
fn = None
with Image.open("Tests/images/hopper.gif") as im:
assert im.fp is not None
fn = im.fp.fileno()
os.fstat(fn)

Expand Down
1 change: 1 addition & 0 deletions docs/example/DdsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class DdsImageFile(ImageFile.ImageFile):
format_description = "DirectDraw Surface"

def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)):
msg = "not a DDS file"
raise SyntaxError(msg)
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/AvifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ def _open(self) -> None:
):
msg = "Invalid opening codec"
raise ValueError(msg)

assert self.fp is not None
self._decoder = _avif.AvifDecoder(
self.fp.read(),
DECODE_CODEC_CHOICE,
Expand Down
1 change: 1 addition & 0 deletions src/PIL/BlpImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class BlpImageFile(ImageFile.ImageFile):
format_description = "Blizzard Mipmap Format"

def _open(self) -> None:
assert self.fp is not None
self.magic = self.fp.read(4)
if not _accept(self.magic):
msg = f"Bad BLP magic {repr(self.magic)}"
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/BmpImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class BmpImageFile(ImageFile.ImageFile):

def _bitmap(self, header: int = 0, offset: int = 0) -> None:
"""Read relevant info about the BMP"""
assert self.fp is not None
read, seek = self.fp.read, self.fp.seek
if header:
seek(header)
Expand Down Expand Up @@ -311,6 +312,7 @@ def _bitmap(self, header: int = 0, offset: int = 0) -> None:
def _open(self) -> None:
"""Open file, check magic number and read header"""
# read 14 bytes: magic number, filesize, reserved, header final offset
assert self.fp is not None
head_data = self.fp.read(14)
# choke if the file does not have the required magic bytes
if not _accept(head_data):
Expand Down
1 change: 1 addition & 0 deletions src/PIL/BufrStubImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
format_description = "BUFR"

def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)):
msg = "Not a BUFR file"
raise SyntaxError(msg)
Expand Down
1 change: 1 addition & 0 deletions src/PIL/DcxImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class DcxImageFile(PcxImageFile):

def _open(self) -> None:
# Header
assert self.fp is not None
s = self.fp.read(4)
if not _accept(s):
msg = "not a DCX file"
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/EpsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class EpsImageFile(ImageFile.ImageFile):
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}

def _open(self) -> None:
assert self.fp is not None
(length, offset) = self._find_offset(self.fp)

# go to offset - start of "%!PS"
Expand Down Expand Up @@ -403,6 +404,7 @@ def load(
) -> Image.core.PixelAccess | None:
# Load EPS via Ghostscript
if self.tile:
assert self.fp is not None
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
self._mode = self.im.mode
self._size = self.im.size
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/FpxImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def _open(self) -> None:
# read the OLE directory and see if this is a likely
# to be a FlashPix file

assert self.fp is not None
try:
self.ole = olefile.OleFileIO(self.fp)
except OSError as e:
Expand Down Expand Up @@ -229,6 +230,7 @@ def _open_subimage(self, index: int = 1, subimage: int = 0) -> None:
if y >= ysize:
break # isn't really required

assert self.fp is not None
self.stream = stream
self._fp = self.fp
self.fp = None
Expand Down
1 change: 1 addition & 0 deletions src/PIL/FtexImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class FtexImageFile(ImageFile.ImageFile):
format_description = "Texture File Format (IW2:EOC)"

def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(4)):
msg = "not an FTEX file"
raise SyntaxError(msg)
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/GbrImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class GbrImageFile(ImageFile.ImageFile):
format_description = "GIMP brush file"

def _open(self) -> None:
assert self.fp is not None
header_size = i32(self.fp.read(4))
if header_size < 20:
msg = "not a GIMP brush"
Expand Down Expand Up @@ -88,6 +89,7 @@ def _open(self) -> None:

def load(self) -> Image.core.PixelAccess | None:
if self._im is None:
assert self.fp is not None
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self._data_size))
return Image.Image.load(self)
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/GifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class GifImageFile(ImageFile.ImageFile):
global_palette = None

def data(self) -> bytes | None:
assert self.fp is not None
s = self.fp.read(1)
if s and s[0]:
return self.fp.read(s[0])
Expand All @@ -100,6 +101,7 @@ def _is_palette_needed(self, p: bytes) -> bool:

def _open(self) -> None:
# Screen
assert self.fp is not None
s = self.fp.read(13)
if not _accept(s):
msg = "not a GIF file"
Expand Down
1 change: 1 addition & 0 deletions src/PIL/GribStubImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
format_description = "GRIB"

def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(8)):
msg = "Not a GRIB file"
raise SyntaxError(msg)
Expand Down
1 change: 1 addition & 0 deletions src/PIL/Hdf5StubImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
format_description = "HDF5"

def _open(self) -> None:
assert self.fp is not None
if not _accept(self.fp.read(8)):
msg = "Not an HDF file"
raise SyntaxError(msg)
Expand Down
1 change: 1 addition & 0 deletions src/PIL/IcnsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ class IcnsImageFile(ImageFile.ImageFile):
format_description = "Mac OS icns resource"

def _open(self) -> None:
assert self.fp is not None
self.icns = IcnsFile(self.fp)
self._mode = "RGBA"
self.info["sizes"] = self.icns.itersizes()
Expand Down
1 change: 1 addition & 0 deletions src/PIL/IcoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ class IcoImageFile(ImageFile.ImageFile):
format_description = "Windows Icon"

def _open(self) -> None:
assert self.fp is not None
self.ico = IcoFile(self.fp)
self.info["sizes"] = self.ico.sizes()
self.size = self.ico.entry[0].dim
Expand Down
1 change: 1 addition & 0 deletions src/PIL/ImImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def _open(self) -> None:
# Quick rejection: if there's not an LF among the first
# 100 bytes, this is (probably) not a text header.

assert self.fp is not None
if b"\n" not in self.fp.read(100):
msg = "not an IM file"
raise SyntaxError(msg)
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,8 @@ def getexif(self) -> Exif:
assert isinstance(self, TiffImagePlugin.TiffImageFile)
self._exif.bigtiff = self.tag_v2._bigtiff
self._exif.endian = self.tag_v2._endian

assert self.fp is not None
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
if exif_info is not None:
self._exif.load(exif_info)
Expand Down
1 change: 1 addition & 0 deletions src/PIL/ImageFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ def load(self) -> Image.core.PixelAccess | None:
self.map: mmap.mmap | None = None
use_mmap = self.filename and len(self.tile) == 1

assert self.fp is not None
readonly = 0

# look for read/seek overrides
Expand Down
3 changes: 3 additions & 0 deletions src/PIL/IptcImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def getint(self, key: tuple[int, int]) -> int:
def field(self) -> tuple[tuple[int, int] | None, int]:
#
# get a IPTC field header
assert self.fp is not None
s = self.fp.read(5)
if not s.strip(b"\x00"):
return None, 0
Expand Down Expand Up @@ -76,6 +77,7 @@ def field(self) -> tuple[tuple[int, int] | None, int]:

def _open(self) -> None:
# load descriptive fields
assert self.fp is not None
while True:
offset = self.fp.tell()
tag, size = self.field()
Expand Down Expand Up @@ -131,6 +133,7 @@ def load(self) -> Image.core.PixelAccess | None:
assert isinstance(args, tuple)
compression, band = args

assert self.fp is not None
self.fp.seek(self.tile[0].offset)

# Copy image data to temporary file
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/Jpeg2KImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
format_description = "JPEG 2000 (ISO 15444)"

def _open(self) -> None:
assert self.fp is not None
sig = self.fp.read(4)
if sig == b"\xff\x4f\xff\x51":
self.codec = "j2k"
Expand Down Expand Up @@ -304,6 +305,7 @@ def _open(self) -> None:
]

def _parse_comment(self) -> None:
assert self.fp is not None
while True:
marker = self.fp.read(2)
if not marker:
Expand Down
7 changes: 7 additions & 0 deletions src/PIL/JpegImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@


def Skip(self: JpegImageFile, marker: int) -> None:
assert self.fp is not None
n = i16(self.fp.read(2)) - 2
ImageFile._safe_read(self.fp, n)

Expand All @@ -70,6 +71,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
# Application marker. Store these in the APP dictionary.
# Also look for well-known application markers.

assert self.fp is not None
n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)

Expand Down Expand Up @@ -174,6 +176,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
def COM(self: JpegImageFile, marker: int) -> None:
#
# Comment marker. Store these in the APP dictionary.
assert self.fp is not None
n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)

Expand All @@ -190,6 +193,7 @@ def SOF(self: JpegImageFile, marker: int) -> None:
# mode. Note that this could be made a bit brighter, by
# looking for JFIF and Adobe APP markers.

assert self.fp is not None
n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)
self._size = i16(s, 3), i16(s, 1)
Expand Down Expand Up @@ -240,6 +244,7 @@ def DQT(self: JpegImageFile, marker: int) -> None:
# FIXME: The quantization tables can be used to estimate the
# compression quality.

assert self.fp is not None
n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)
while len(s):
Expand Down Expand Up @@ -340,6 +345,7 @@ class JpegImageFile(ImageFile.ImageFile):
format_description = "JPEG (ISO 10918)"

def _open(self) -> None:
assert self.fp is not None
s = self.fp.read(3)

if not _accept(s):
Expand Down Expand Up @@ -408,6 +414,7 @@ def load_read(self, read_bytes: int) -> bytes:
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
so libjpeg can finish decoding
"""
assert self.fp is not None
s = self.fp.read(read_bytes)

if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"):
Expand Down
1 change: 1 addition & 0 deletions src/PIL/MicImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def _open(self) -> None:
self._n_frames = len(self.images)
self.is_animated = self._n_frames > 1

assert self.fp is not None
self.__fp = self.fp
self.seek(0)

Expand Down
2 changes: 2 additions & 0 deletions src/PIL/MpoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
_close_exclusive_fp_after_loading = False

def _open(self) -> None:
assert self.fp is not None
self.fp.seek(0) # prep the fp in order to pass the JPEG test
JpegImagePlugin.JpegImageFile._open(self)
self._after_jpeg_open()
Expand All @@ -125,6 +126,7 @@ def _after_jpeg_open(self, mpheader: dict[int, Any] | None = None) -> None:
assert self.n_frames == len(self.__mpoffsets)
del self.info["mpoffset"] # no longer needed
self.is_animated = self.n_frames > 1
assert self.fp is not None
self._fp = self.fp # FIXME: hack
self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame
self.__frame = 0
Expand Down
Loading
Loading