From 8a36c5e73ec781fa9ea78e32018e227538fd6011 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 28 Jan 2025 12:19:48 +0200 Subject: [PATCH 1/4] Defer importing tempfile --- src/PIL/EpsImagePlugin.py | 3 ++- src/PIL/Image.py | 3 ++- src/PIL/ImageGrab.py | 3 ++- src/PIL/JpegImagePlugin.py | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 8717e4df55b..252849cf6a9 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -26,7 +26,6 @@ import re import subprocess import sys -import tempfile from . import Image, ImageFile from ._binary import i32le as i32 @@ -95,6 +94,8 @@ def Ghostscript( res_x = 72.0 * width / (bbox[2] - bbox[0]) res_y = 72.0 * height / (bbox[3] - bbox[1]) + import tempfile + out_fd, outfile = tempfile.mkstemp() os.close(out_fd) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c50cb777981..7a0014fc1a6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -36,7 +36,6 @@ import re import struct import sys -import tempfile import warnings from collections.abc import MutableMapping from enum import IntEnum @@ -742,6 +741,8 @@ def _dump( if not filename.endswith(suffix): filename += suffix else: + import tempfile + f, filename = tempfile.mkstemp(suffix) os.close(f) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 52cbfea406d..03091e756e5 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -21,7 +21,6 @@ import shutil import subprocess import sys -import tempfile from . import Image @@ -40,6 +39,8 @@ def grab( scale_down: bool = False, ) -> Image.Image: im: Image.Image + import tempfile + if xdisplay is None: if sys.platform == "darwin": fh, filepath = tempfile.mkstemp(".png") diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 5d4347818d4..98f19ee8e5e 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -40,7 +40,6 @@ import struct import subprocess import sys -import tempfile import warnings from . import Image, ImageFile @@ -467,6 +466,7 @@ def draft( def load_djpeg(self) -> None: # ALTERNATIVE: handle JPEGs via the IJG command line utilities + import tempfile f, path = tempfile.mkstemp() os.close(f) From c8d2165973fff8692006540b486bb065f4332be5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 29 Jun 2026 17:48:23 +0300 Subject: [PATCH 2/4] Use lazy imports on Python 3.15 to improve startup speed --- src/PIL/AvifImagePlugin.py | 2 ++ src/PIL/BlpImagePlugin.py | 2 ++ src/PIL/BmpImagePlugin.py | 2 ++ src/PIL/CurImagePlugin.py | 2 ++ src/PIL/DcxImagePlugin.py | 2 ++ src/PIL/DdsImagePlugin.py | 2 ++ src/PIL/EpsImagePlugin.py | 2 ++ src/PIL/FitsImagePlugin.py | 2 ++ src/PIL/FliImagePlugin.py | 2 ++ src/PIL/FpxImagePlugin.py | 2 ++ src/PIL/FtexImagePlugin.py | 2 ++ src/PIL/GbrImagePlugin.py | 2 ++ src/PIL/GdImageFile.py | 2 ++ src/PIL/GifImagePlugin.py | 8 ++++++++ src/PIL/GimpGradientFile.py | 2 ++ src/PIL/GimpPaletteFile.py | 2 ++ src/PIL/IcnsImagePlugin.py | 2 ++ src/PIL/IcoImagePlugin.py | 2 ++ src/PIL/ImImagePlugin.py | 2 ++ src/PIL/Image.py | 10 ++++++++++ src/PIL/ImageColor.py | 2 ++ src/PIL/ImageDraw.py | 2 ++ src/PIL/ImageFile.py | 2 ++ src/PIL/ImageFont.py | 2 ++ src/PIL/ImageGrab.py | 2 ++ src/PIL/ImageMorph.py | 2 ++ src/PIL/ImageOps.py | 2 ++ src/PIL/ImagePalette.py | 2 ++ src/PIL/ImageQt.py | 2 ++ src/PIL/ImageShow.py | 2 ++ src/PIL/ImageStat.py | 2 ++ src/PIL/ImageText.py | 2 ++ src/PIL/ImageTk.py | 2 ++ src/PIL/IptcImagePlugin.py | 2 ++ src/PIL/Jpeg2KImagePlugin.py | 2 ++ src/PIL/JpegImagePlugin.py | 11 +++++++++++ src/PIL/McIdasImagePlugin.py | 2 ++ src/PIL/MicImagePlugin.py | 2 ++ src/PIL/MpegImagePlugin.py | 2 ++ src/PIL/MpoImagePlugin.py | 2 ++ src/PIL/MspImagePlugin.py | 2 ++ src/PIL/PaletteFile.py | 2 ++ src/PIL/PalmImagePlugin.py | 2 ++ src/PIL/PcfFontFile.py | 2 ++ src/PIL/PcxImagePlugin.py | 2 ++ src/PIL/PdfImagePlugin.py | 2 ++ src/PIL/PdfParser.py | 2 ++ src/PIL/PixarImagePlugin.py | 2 ++ src/PIL/PngImagePlugin.py | 10 ++++++++++ src/PIL/PpmImagePlugin.py | 2 ++ src/PIL/PsdImagePlugin.py | 2 ++ src/PIL/QoiImagePlugin.py | 2 ++ src/PIL/SgiImagePlugin.py | 2 ++ src/PIL/SpiderImagePlugin.py | 2 ++ src/PIL/SunImagePlugin.py | 2 ++ src/PIL/TarIO.py | 2 ++ src/PIL/TgaImagePlugin.py | 2 ++ src/PIL/TiffImagePlugin.py | 10 ++++++++++ src/PIL/WalImageFile.py | 2 ++ src/PIL/WebPImagePlugin.py | 2 ++ src/PIL/WmfImagePlugin.py | 2 ++ src/PIL/XpmImagePlugin.py | 2 ++ src/PIL/_binary.py | 2 ++ src/PIL/_deprecate.py | 2 ++ src/PIL/_typing.py | 2 ++ src/PIL/features.py | 2 ++ 66 files changed, 171 insertions(+) diff --git a/src/PIL/AvifImagePlugin.py b/src/PIL/AvifImagePlugin.py index 6e7d0f738b3..73d3758971f 100644 --- a/src/PIL/AvifImagePlugin.py +++ b/src/PIL/AvifImagePlugin.py @@ -1,5 +1,7 @@ from __future__ import annotations +__lazy_modules__ = {"io", "typing"} + import os from io import BytesIO from typing import IO diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index fddfd1fd377..63390a9776a 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -31,6 +31,8 @@ from __future__ import annotations +__lazy_modules__ = {"io", "struct"} + import abc import os import struct diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index b6b1a7f1e81..354b1c4150e 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -24,6 +24,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + import os from . import Image, ImageFile, ImagePalette diff --git a/src/PIL/CurImagePlugin.py b/src/PIL/CurImagePlugin.py index b20425040f4..f74e063d217 100644 --- a/src/PIL/CurImagePlugin.py +++ b/src/PIL/CurImagePlugin.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import BmpImagePlugin, Image, ImageOps from ._binary import i16le as i16 from ._binary import i32le as i32 diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index d3f456ddcc4..0864680d128 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -22,6 +22,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "PIL._util"} + from . import Image from ._binary import i32le as i32 from ._util import DeferredError diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index e09a2b867a1..f3613e043d5 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -12,6 +12,8 @@ from __future__ import annotations +__lazy_modules__ = {"struct"} + import struct import sys from enum import IntEnum, IntFlag diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 252849cf6a9..67209a7b9b9 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -21,6 +21,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "io", "subprocess"} + import io import os import re diff --git a/src/PIL/FitsImagePlugin.py b/src/PIL/FitsImagePlugin.py index dca61c2e21b..b2af17fefe1 100644 --- a/src/PIL/FitsImagePlugin.py +++ b/src/PIL/FitsImagePlugin.py @@ -10,6 +10,8 @@ # from __future__ import annotations +__lazy_modules__ = {"gzip", "math"} + import gzip import math diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index da1e8e95cf3..43e67ee1f35 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -16,6 +16,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "PIL._util"} + import os from . import Image, ImageFile, ImagePalette diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index f7eafe4d5e7..16eb3a9d3b7 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -16,6 +16,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "olefile"} + import olefile from . import Image, ImageFile diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index 3137d200129..6ae35454d72 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -53,6 +53,8 @@ from __future__ import annotations +__lazy_modules__ = {"io", "struct"} + import struct from enum import IntEnum from io import BytesIO diff --git a/src/PIL/GbrImagePlugin.py b/src/PIL/GbrImagePlugin.py index ec666c81c2c..a92f108224e 100644 --- a/src/PIL/GbrImagePlugin.py +++ b/src/PIL/GbrImagePlugin.py @@ -25,6 +25,8 @@ # the color depth field. This is currently unsupported by Pillow. from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile from ._binary import i32be as i32 diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index c8ba8784560..118d4cbb756 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -28,6 +28,8 @@ class is not registered for use with :py:func:`PIL.Image.open()`. To open a from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile, ImagePalette, UnidentifiedImageError from ._binary import i16be as i16 from ._binary import i32be as i32 diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index e686dc80851..661cddba0c6 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -25,6 +25,14 @@ # from __future__ import annotations +__lazy_modules__ = { + "PIL._binary", + "PIL._util", + "itertools", + "math", + "subprocess", +} + import itertools import math import os diff --git a/src/PIL/GimpGradientFile.py b/src/PIL/GimpGradientFile.py index fb958721882..5b2cc94de23 100644 --- a/src/PIL/GimpGradientFile.py +++ b/src/PIL/GimpGradientFile.py @@ -21,6 +21,8 @@ from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "math"} + from math import log, pi, sin, sqrt from ._binary import o8 diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index 5dc83086dd1..36d882faaf7 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -15,6 +15,8 @@ # from __future__ import annotations +__lazy_modules__ = {"io", "re"} + import re from io import BytesIO diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 6135e0693aa..bd528f08343 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -18,6 +18,8 @@ # from __future__ import annotations +__lazy_modules__ = {"io", "struct"} + import io import os import struct diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index c145d251e1c..62cefbb529e 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -36,6 +36,8 @@ # * https://msdn.microsoft.com/en-us/library/ms997538.aspx from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "io", "math", "warnings"} + import warnings from io import BytesIO from math import ceil, log diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 78861f3edfb..253496a22b6 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -26,6 +26,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._util"} + import os import re diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 7a0014fc1a6..c6e67afcac0 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -26,6 +26,16 @@ from __future__ import annotations +__lazy_modules__ = { + "PIL._binary", + "PIL._deprecate", + "PIL._util", + "io", + "math", + "re", + "struct", +} + import abc import atexit import builtins diff --git a/src/PIL/ImageColor.py b/src/PIL/ImageColor.py index c2a84100f54..c2aecbdf2aa 100644 --- a/src/PIL/ImageColor.py +++ b/src/PIL/ImageColor.py @@ -18,6 +18,8 @@ # from __future__ import annotations +__lazy_modules__ = {"re"} + import re from functools import lru_cache diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 5503f6f5a3b..fa7104721c2 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -31,6 +31,8 @@ # from __future__ import annotations +__lazy_modules__ = {"math", "struct"} + import math import struct from typing import cast diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 14038e4732d..cd6b26b92c7 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -28,6 +28,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._util", "io", "itertools", "struct"} + import abc import io import itertools diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 1f5fe66197a..0c9e7402f62 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -27,6 +27,8 @@ from __future__ import annotations +__lazy_modules__ = {"base64", "io", "types", "warnings"} + import abc import base64 import os diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 03091e756e5..da46eb1eaab 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -16,6 +16,8 @@ # from __future__ import annotations +__lazy_modules__ = {"io", "shutil", "subprocess"} + import io import os import shutil diff --git a/src/PIL/ImageMorph.py b/src/PIL/ImageMorph.py index 104d029efc2..ed15abc5f23 100644 --- a/src/PIL/ImageMorph.py +++ b/src/PIL/ImageMorph.py @@ -6,6 +6,8 @@ # Copyright (c) 2014 Dov Grobgeld from __future__ import annotations +__lazy_modules__ = {"re"} + import re from . import Image, _imagingmorph diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 19fae47bcb7..0141f57dd7d 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -18,6 +18,8 @@ # from __future__ import annotations +__lazy_modules__ = {"re"} + import re from typing import Protocol, cast, overload diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index c25d80e47c1..6eff2eeeb72 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"array"} + import array from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index af4d0742d6b..05534a8c753 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._util", "io"} + import sys from io import BytesIO diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index f24f4cf1056..b83079a3406 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -13,6 +13,8 @@ # from __future__ import annotations +__lazy_modules__ = {"shlex", "subprocess"} + import abc import os import shutil diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index 3a1044ba449..fde0256b2f8 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -22,6 +22,8 @@ # from __future__ import annotations +__lazy_modules__ = {"math"} + import math from functools import cached_property diff --git a/src/PIL/ImageText.py b/src/PIL/ImageText.py index 327dc87307c..1374d2cb485 100644 --- a/src/PIL/ImageText.py +++ b/src/PIL/ImageText.py @@ -1,5 +1,7 @@ from __future__ import annotations +__lazy_modules__ = {"math", "re"} + import math import re from typing import AnyStr, Generic, NamedTuple diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index ad7f841db25..f2dd618cd01 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -26,6 +26,8 @@ # from __future__ import annotations +__lazy_modules__ = {"io", "tkinter"} + import tkinter from io import BytesIO diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index d00450a7055..becafe641ec 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -16,6 +16,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "io", "typing"} + from io import BytesIO from typing import cast diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 13f7cc774f0..908ca7f2afc 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -15,6 +15,8 @@ # from __future__ import annotations +__lazy_modules__ = {"io", "struct"} + import io import os import struct diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 98f19ee8e5e..6157d0c230d 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -33,6 +33,17 @@ # from __future__ import annotations +__lazy_modules__ = { + "PIL.JpegPresets", + "PIL._binary", + "array", + "io", + "math", + "struct", + "subprocess", + "warnings", +} + import array import io import math diff --git a/src/PIL/McIdasImagePlugin.py b/src/PIL/McIdasImagePlugin.py index 9a47933b69c..e0f3b404b7c 100644 --- a/src/PIL/McIdasImagePlugin.py +++ b/src/PIL/McIdasImagePlugin.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"struct"} + import struct from . import Image, ImageFile diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 99a07bae02c..f8cf3a76fd9 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"olefile"} + import olefile from . import Image, TiffImagePlugin diff --git a/src/PIL/MpegImagePlugin.py b/src/PIL/MpegImagePlugin.py index 666179c68cc..ad6a0d06427 100644 --- a/src/PIL/MpegImagePlugin.py +++ b/src/PIL/MpegImagePlugin.py @@ -14,6 +14,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile from ._binary import i8 diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 69374482dd3..5754415b824 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -19,6 +19,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "PIL._util", "struct"} + import os import struct from typing import cast diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 24cece0b60c..921519f0694 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -24,6 +24,8 @@ # See also: https://www.fileformat.info/format/mspaint/egff.htm from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "io", "struct"} + import io import struct diff --git a/src/PIL/PaletteFile.py b/src/PIL/PaletteFile.py index f7b4e708afa..8d87fee4195 100644 --- a/src/PIL/PaletteFile.py +++ b/src/PIL/PaletteFile.py @@ -14,6 +14,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from ._binary import o8 TYPE_CHECKING = False diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index 438ebec29f6..100e23162ba 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -8,6 +8,8 @@ ## from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile from ._binary import o8 from ._binary import o16be as o16b diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index f12506a6666..6b1c0653992 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "io"} + import io from . import FontFile, Image diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py index 74ff1f431ba..3fd4cb19b30 100644 --- a/src/PIL/PcxImagePlugin.py +++ b/src/PIL/PcxImagePlugin.py @@ -26,6 +26,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "io"} + import io import logging diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index f01e43fa1fd..67c6edaee91 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -21,6 +21,8 @@ ## from __future__ import annotations +__lazy_modules__ = {"io", "math"} + import io import math import os diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index a75f43a354a..67255719035 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -1,5 +1,7 @@ from __future__ import annotations +__lazy_modules__ = {"calendar", "mmap", "zlib"} + import calendar import codecs import collections diff --git a/src/PIL/PixarImagePlugin.py b/src/PIL/PixarImagePlugin.py index d2b6d0a97e4..d2cff36aed3 100644 --- a/src/PIL/PixarImagePlugin.py +++ b/src/PIL/PixarImagePlugin.py @@ -20,6 +20,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile from ._binary import i16le as i16 diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index af4f0381aa1..0d337aa281b 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -32,6 +32,16 @@ # from __future__ import annotations +__lazy_modules__ = { + "PIL._binary", + "PIL._util", + "fractions", + "itertools", + "struct", + "warnings", + "zlib", +} + import itertools import logging import re diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 05a79f15a7f..5b0feef9204 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -15,6 +15,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "math"} + import math from . import Image, ImageFile diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index f3f0714bd26..c9af35ca112 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "PIL._util", "io"} + import io from functools import cached_property diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index 3226c46c509..fb4afeef471 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -7,6 +7,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "typing"} + import os from typing import IO diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index cd022a0b5d5..178739ba19e 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -22,6 +22,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "struct"} + import os import struct diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 54dfa79359c..d210c5f1af4 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -34,6 +34,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._util", "struct"} + import os import struct import sys diff --git a/src/PIL/SunImagePlugin.py b/src/PIL/SunImagePlugin.py index 8912379ea3e..8a0dcf1a148 100644 --- a/src/PIL/SunImagePlugin.py +++ b/src/PIL/SunImagePlugin.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile, ImagePalette from ._binary import i32be as i32 diff --git a/src/PIL/TarIO.py b/src/PIL/TarIO.py index 86490a496f3..68daf6616a0 100644 --- a/src/PIL/TarIO.py +++ b/src/PIL/TarIO.py @@ -15,6 +15,8 @@ # from __future__ import annotations +__lazy_modules__ = {"io"} + import io from . import ContainerIO diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index c25e2b9ca08..5d960a20ab7 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -17,6 +17,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary", "warnings"} + import os import warnings diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 00e460238e6..c11f034f71e 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -40,6 +40,16 @@ # from __future__ import annotations +__lazy_modules__ = { + "PIL._binary", + "PIL._util", + "fractions", + "itertools", + "math", + "struct", + "warnings", +} + import io import itertools import logging diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py index a97872ff61a..57c10f8cccf 100644 --- a/src/PIL/WalImageFile.py +++ b/src/PIL/WalImageFile.py @@ -25,6 +25,8 @@ from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile from ._binary import i32le as i32 diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 63a48169182..fe320a94a78 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -1,5 +1,7 @@ from __future__ import annotations +__lazy_modules__ = {"io"} + from io import BytesIO from . import Image, ImageFile diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index fb49bc5a433..1815933b4c0 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -20,6 +20,8 @@ # http://wvware.sourceforge.net/caolan/ora-wmf.html from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + from . import Image, ImageFile from ._binary import i16le as word from ._binary import si16le as short diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index 07fbd8e4e4e..839d41572e9 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -15,6 +15,8 @@ # from __future__ import annotations +__lazy_modules__ = {"PIL._binary"} + import re from . import Image, ImageFile, ImagePalette diff --git a/src/PIL/_binary.py b/src/PIL/_binary.py index d3236c17abb..23d82d79632 100644 --- a/src/PIL/_binary.py +++ b/src/PIL/_binary.py @@ -16,6 +16,8 @@ from __future__ import annotations +__lazy_modules__ = {"struct"} + from struct import pack, unpack_from diff --git a/src/PIL/_deprecate.py b/src/PIL/_deprecate.py index ff18bbea2e5..8bee7c4e909 100644 --- a/src/PIL/_deprecate.py +++ b/src/PIL/_deprecate.py @@ -1,5 +1,7 @@ from __future__ import annotations +__lazy_modules__ = {"warnings"} + import warnings from . import __version__ diff --git a/src/PIL/_typing.py b/src/PIL/_typing.py index 673c29ea01c..7b60d45682f 100644 --- a/src/PIL/_typing.py +++ b/src/PIL/_typing.py @@ -1,5 +1,7 @@ from __future__ import annotations +__lazy_modules__ = {"numbers", "types"} + import os import sys from collections.abc import Sequence diff --git a/src/PIL/features.py b/src/PIL/features.py index 8d72d92cb05..a7cf7318eaa 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -1,5 +1,7 @@ from __future__ import annotations +__lazy_modules__ = {"collections", "warnings"} + import collections import os import sys From d0f7ae7481820f70383ca3f32bed1dee7a5cba64 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:44:15 +0300 Subject: [PATCH 3/4] Defer importing defusedxml --- src/PIL/Image.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c6e67afcac0..45e6fb7d798 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -66,16 +66,9 @@ from ._deprecate import deprecate from ._util import DeferredError, is_path -ElementTree: ModuleType | None -try: - from defusedxml import ElementTree -except ImportError: - ElementTree = None - TYPE_CHECKING = False if TYPE_CHECKING: from collections.abc import Callable, Iterator, Sequence - from types import ModuleType from typing import Any, Literal, Self logger = logging.getLogger(__name__) @@ -1599,6 +1592,11 @@ def getxmp(self, *, strip_namespaces: bool = True) -> dict[str, Any]: :returns: XMP tags in a dictionary. """ + try: + from defusedxml import ElementTree + except ImportError: + warnings.warn("XMP data cannot be read without defusedxml dependency") + return {} if strip_namespaces: @@ -1630,9 +1628,6 @@ def get_value(element: Element) -> str | dict[str, Any] | None: return element.text return value - if ElementTree is None: - warnings.warn("XMP data cannot be read without defusedxml dependency") - return {} if "xmp" not in self.info: return {} root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00 ")) From 2c10c4f3c778c479108681737efd54faf3bf4e99 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 9 Aug 2026 14:40:49 +0300 Subject: [PATCH 4/4] Drop debug logging from Image --- src/PIL/Image.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 45e6fb7d798..5ec8833b120 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -40,7 +40,6 @@ import atexit import builtins import io -import logging import math import os import re @@ -71,8 +70,6 @@ from collections.abc import Callable, Iterator, Sequence from typing import Any, Literal, Self -logger = logging.getLogger(__name__) - class DecompressionBombWarning(RuntimeWarning): pass @@ -424,11 +421,9 @@ def _import_plugin_for_extension(ext: str | bytes) -> bool: return False try: - logger.debug("Importing %s", plugin) __import__(f"{__spec__.parent}.{plugin}", globals(), locals(), []) return True - except ImportError as e: - logger.debug("Image: failed to import %s: %s", plugin, e) + except ImportError: return False @@ -492,10 +487,9 @@ def init() -> bool: for plugin in _plugins: try: - logger.debug("Importing %s", plugin) __import__(f"{__spec__.parent}.{plugin}", globals(), locals(), []) - except ImportError as e: - logger.debug("Image: failed to import %s: %s", plugin, e) + except ImportError: + pass if OPEN or SAVE: _initialized = 2