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
20 changes: 12 additions & 8 deletions Tests/test_file_iptc.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,28 @@ def test_i():
c = b"a"

# Act
ret = IptcImagePlugin.i(c)
with pytest.warns(DeprecationWarning):
ret = IptcImagePlugin.i(c)

# Assert
assert ret == 97


def test_dump():
def test_dump(monkeypatch):
# Arrange
c = b"abc"
# Temporarily redirect stdout
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
mystdout = StringIO()
monkeypatch.setattr(sys, "stdout", mystdout)

# Act
IptcImagePlugin.dump(c)

# Reset stdout
sys.stdout = old_stdout
with pytest.warns(DeprecationWarning):
IptcImagePlugin.dump(c)

# Assert
assert mystdout.getvalue() == "61 62 63 \n"


def test_pad_deprecation():
with pytest.warns(DeprecationWarning):
assert IptcImagePlugin.PAD == b"\0\0\0\0"
11 changes: 11 additions & 0 deletions docs/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ ImageFile.raise_oserror
error codes returned by a codec's ``decode()`` method, which ImageFile already does
automatically.

IptcImageFile helper functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. deprecated:: 10.2.0

The functions ``IptcImageFile.dump`` and ``IptcImageFile.i``, and the constant
``IptcImageFile.PAD`` have been deprecated and will be removed in Pillow
12.0.0 (2025-10-15). These are undocumented helper functions intended
for internal use, so there is no replacement. They can each be replaced
by a single line of code using builtin functions in Python.

Removed features
----------------

Expand Down
12 changes: 8 additions & 4 deletions docs/releasenotes/10.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ ImageFile.raise_oserror
error codes returned by a codec's ``decode()`` method, which ImageFile already does
automatically.

TODO
^^^^

TODO
IptcImageFile helper functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The functions ``IptcImageFile.dump`` and ``IptcImageFile.i``, and the constant
``IptcImageFile.PAD`` have been deprecated and will be removed in Pillow
12.0.0 (2025-10-15). These are undocumented helper functions intended
for internal use, so there is no replacement. They can each be replaced
by a single line of code using builtin functions in Python.

API Changes
===========
Expand Down
6 changes: 3 additions & 3 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3190,7 +3190,7 @@ def _decompression_bomb_check(size):
)


def open(fp, mode="r", formats=None):
def open(fp, mode="r", formats=None) -> Image:
"""
Opens and identifies the given image file.

Expand Down Expand Up @@ -3415,7 +3415,7 @@ def merge(mode, bands):
# Plugin registry


def register_open(id, factory, accept=None):
def register_open(id, factory, accept=None) -> None:
"""
Register an image file plugin. This function should not be used
in application code.
Expand Down Expand Up @@ -3469,7 +3469,7 @@ def register_save_all(id, driver):
SAVE_ALL[id.upper()] = driver


def register_extension(id, extension):
def register_extension(id, extension) -> None:
"""
Registers an image extension. This function should not be
used in application code.
Expand Down
34 changes: 25 additions & 9 deletions src/PIL/IptcImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,45 @@

import os
import tempfile
from typing import Sequence

from . import Image, ImageFile
from ._binary import i16be as i16
from ._binary import i32be as i32
from ._deprecate import deprecate

COMPRESSION = {1: "raw", 5: "jpeg"}

PAD = b"\0\0\0\0"

def __getattr__(name: str) -> bytes:
if name == "PAD":
deprecate("IptcImagePlugin.PAD", 12)
return b"\0\0\0\0"
msg = f"module '{__name__}' has no attribute '{name}'"
raise AttributeError(msg)


#
# Helpers


def _i(c: bytes) -> int:
return i32((b"\0\0\0\0" + c)[-4:])


def _i8(c: int | bytes) -> int:
return c if isinstance(c, int) else c[0]


def i(c):
return i32((PAD + c)[-4:])
def i(c: bytes) -> int:
""".. deprecated:: 10.2.0"""
deprecate("IptcImagePlugin.i", 12)
return _i(c)


def dump(c):
def dump(c: Sequence[int | bytes]) -> None:
""".. deprecated:: 10.2.0"""
deprecate("IptcImagePlugin.dump", 12)
for i in c:
print("%02x" % _i8(i), end=" ")
print()
Expand All @@ -55,10 +71,10 @@ class IptcImageFile(ImageFile.ImageFile):
format = "IPTC"
format_description = "IPTC/NAA"

def getint(self, key):
return i(self.info[key])
def getint(self, key: tuple[int, int]) -> int:
return _i(self.info[key])

def field(self):
def field(self) -> tuple[tuple[int, int] | None, int]:
#
# get a IPTC field header
s = self.fp.read(5)
Expand All @@ -80,13 +96,13 @@ def field(self):
elif size == 128:
size = 0
elif size > 128:
size = i(self.fp.read(size - 128))
size = _i(self.fp.read(size - 128))
else:
size = i16(s, 3)

return tag, size

def _open(self):
def _open(self) -> None:
# load descriptive fields
while True:
offset = self.fp.tell()
Expand Down