Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ def test_open_formats(self) -> None:
assert im.mode == "RGB"
assert im.size == (128, 128)

@pytest.mark.parametrize("formats", (("!PNG",), ("PNG", "!PNG")))
def test_open_formats_exclude(self, formats: tuple[str]) -> None:
with pytest.raises(UnidentifiedImageError):
with Image.open("Tests/images/hopper.png", formats=formats):
pass

def test_open_verbose_failure(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(Image, "WARN_POSSIBLE_FORMATS", True)

Expand Down
52 changes: 26 additions & 26 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3598,11 +3598,15 @@ def open(
and be opened in binary mode. The file object will also seek to zero
before reading.
:param mode: The mode. If given, this argument must be "r".
:param formats: A list or tuple of formats to attempt to load the file in.
This can be used to restrict the set of formats checked.
Pass ``None`` to try all supported formats. You can print the set of
available formats by running ``python3 -m PIL`` or using
the :py:func:`PIL.features.pilinfo` function.
:param formats: A list or tuple of formats to attempt to load the file in, e.g.
("JPEG", "GIF"). This can be used to restrict the set of formats checked.

To exclude a format, start the format with "!", e.g. ("!EPS", "!PSD").
Comment thread
radarhere marked this conversation as resolved.
Outdated

Pass ``None`` to try all supported formats.

You can print the set of available formats by running ``python3 -m PIL`` or
using the :py:func:`PIL.features.pilinfo` function.
:returns: An :py:class:`~PIL.Image.Image` object.
:exception FileNotFoundError: If the file cannot be found.
:exception PIL.UnidentifiedImageError: If the image cannot be opened and
Expand All @@ -3622,11 +3626,12 @@ def open(
)
raise ValueError(msg)

if formats is None:
formats = ID
elif not isinstance(formats, (list, tuple)):
msg = "formats must be a list or tuple" # type: ignore[unreachable]
raise TypeError(msg)
if formats is not None:
if not isinstance(formats, (list, tuple)):
msg = "formats must be a list or tuple" # type: ignore[unreachable]
raise TypeError(msg)
formats = tuple(format.upper() for format in formats)
exclude = all(format.startswith("!") for format in formats)
Comment thread
radarhere marked this conversation as resolved.
Outdated

exclusive_fp = False
filename: str | bytes = ""
Expand Down Expand Up @@ -3657,12 +3662,15 @@ def _open_core(
fp: IO[bytes],
filename: str | bytes,
prefix: bytes,
formats: list[str] | tuple[str, ...],
check_formats: list[str],
) -> ImageFile.ImageFile | None:
for i in formats:
i = i.upper()
if i not in OPEN:
init()
for i in check_formats:
if formats is not None:
if exclude:
if "!" + i in formats:
continue
elif i not in formats or "!" + i in formats:
continue
Comment thread
radarhere marked this conversation as resolved.
Outdated
try:
factory, accept = OPEN[i]
result = not accept or accept(prefix)
Expand All @@ -3682,21 +3690,13 @@ def _open_core(
raise
return None

im = _open_core(fp, filename, prefix, formats)

if im is None and formats is ID:
if im := _open_core(fp, filename, prefix, ID):
Comment thread
radarhere marked this conversation as resolved.
Outdated
# Try preinit (few common plugins) then init (all plugins)
for loader in (preinit, init):
checked_formats = ID.copy()
loader()
if formats != checked_formats:
im = _open_core(
fp,
filename,
prefix,
tuple(f for f in formats if f not in checked_formats),
)
if im is not None:
if check_formats := [f for f in ID if f not in checked_formats]:
if im := _open_core(fp, filename, prefix, check_formats):
break

if im:
Expand Down