From ab75ef66299e7f9cd232d3b6b9335ce2bbb964f8 Mon Sep 17 00:00:00 2001 From: Luke Granger-Brown Date: Tue, 3 Jun 2025 12:00:05 +0100 Subject: [PATCH] Add typing information to ImageCmsProfile, and deprecate product_name/product_info The _set method is no longer necessary, since we no longer compute any attributes from the profile. In most cases, we only set the profile, and in only one branch do we set the filename to anything non-None. product_name/product_info were set to None at some point during what appears to be a batch of changes for Python 3 compatibility (ce041fd1995fe95de86804c83d100ed7d8ecdb3b), and never set back. Given this, let's deprecate these and schedule them for removal in Pillow 13. --- docs/deprecations.rst | 23 +++++++++++++++++++++++ src/PIL/ImageCms.py | 34 ++++++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 0490ba439..29a7a62c2 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -193,6 +193,29 @@ Image.Image.get_child_images() method uses an image's file pointer, and so child images could only be retrieved from an :py:class:`PIL.ImageFile.ImageFile` instance. +ImageCms.ImageCmsProfile._set +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 11.3.0 + +``ImageCms.ImageCmsProfile._set()`` has been deprecated, and will be removed in +Pillow 13 (2026-10-15). You should construct a new ``ImageCmsProfile`` instance +instead. + +ImageCms.ImageCmsProfile.product_name and .product_info +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 11.3.0 + +``ImageCms.ImageCmsProfile.product_name`` and the corresponding +``.product_info`` attributes have been deprecated, and will be removed in +Pillow 13 (2026-10-15). These attributes can be accessed on the ``.profile`` +attribute of ``ImageCmsProfile`` instead. + +Note that ``.product_name`` and ``.product_info`` have been set to ``None`` on +``ImageCmsProfile`` since Pillow 2.3.0 (2014-01-01), so any working code that +makes use of this data will already access it on ``.profile``. + Removed features ---------------- diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index fdfbee789..d9a47aa9b 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -241,6 +241,9 @@ _FLAGS = { class ImageCmsProfile: + profile: core.CmsProfile + filename: str | None + def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: """ :param profile: Either a string representing a filename, @@ -248,6 +251,7 @@ class ImageCmsProfile: low-level profile object """ + self.filename = None if isinstance(profile, str): if sys.platform == "win32": @@ -256,22 +260,40 @@ class ImageCmsProfile: profile_bytes_path.decode("ascii") except UnicodeDecodeError: with open(profile, "rb") as f: - self._set(core.profile_frombytes(f.read())) + self.profile = core.profile_frombytes(f.read()) return - self._set(core.profile_open(profile), profile) + self.filename = profile + self.profile = core.profile_open(profile) elif hasattr(profile, "read"): - self._set(core.profile_frombytes(profile.read())) + self.profile = core.profile_frombytes(profile.read()) elif isinstance(profile, core.CmsProfile): - self._set(profile) + self.profile = profile else: msg = "Invalid type for Profile" # type: ignore[unreachable] raise TypeError(msg) + def __getattr__(self, attr: str) -> Any: + if attr in ("product_name", "product_info"): + deprecate( + f"ImageCms.ImageCmsProfile.{attr}", + 13, + action=( + f"Use ImageCms.ImageCmsProfile.profile.{attr} instead. " + f"Note that {attr} has been set to 'None' since Pillow 2.3.0." + ), + ) + return None + msg = f"'{self.__class__.__name__}' has no attribute '{attr}'" + raise AttributeError(msg) + def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None: + deprecate( + "ImageCmsProfile._set", + 13, + action="Set the 'profile' and 'filename' attributes directly instead.", + ) self.profile = profile self.filename = filename - self.product_name = None # profile.product_name - self.product_info = None # profile.product_info def tobytes(self) -> bytes: """