You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The first commit here defers the import of tempfile in four files to the place where it's actually used, so we don't need to pay the import time if we're not using it. This helps for all Python versions.
So for example, only importing PIL.Image on Python 3.14:
If you put the new lazy keyword before an import, it won't be actually imported until first use. But lazy is only for 3.15+, it's a syntax error in 3.10-3.14.
For code that cannot use the lazy keyword directly (for example, when supporting Python versions older than 3.15 while still using lazy imports on 3.15+), a module can define __lazy_modules__ as a container of fully qualified module name strings. Regular import statements for those modules are then treated as lazy, with the same semantics as the lazy keyword
So the second commit introduces __lazy_modules__.
We can only put top-level imports here, and not those from function or in try/except. And we don't need to put any that used right away at the top-level, like logging in Image.py, but it's not a big deal if we do.
It's certainly a bigger and unrelated change, but looks like Pillow only uses logging for a couple debug calls here and there in PIL.Image -- could be worth it to just get rid of it there (though real-world apps will generally probably import logging somehow either way, so additional laziness would need to be put in there in the stdlib...).
I made the olefile import eager again: it's an optional dependency and lazy imports affect how its related plugins get registered.
I'll have a look at logging.
Unfortunately it's not straightforward to lazy import traceback in the stdib, because if we get an exception during shutdown, the import machinery might already be shutdown: python/cpython#150073.
lazy import re in logging might be more doable, but in any case won't show up until Python 3.16.
--- a/src/PIL/Image.py+++ b/src/PIL/Image.py-import logging
...
-logger = logging.getLogger(__name__)+class _LazyLogger:+ """+ Defer importing `logging` until the logger is first used to avoid slow import.+ """++ def __getattr__(self, attr: str) -> Any:+ import logging++ logger = logging.getLogger(__name__)+ globals()["logger"] = logger+ return getattr(logger, attr)+++logger = _LazyLogger()
Before: 16 ms
After: 12 ms
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Defer
The first commit here defers the import of
tempfilein four files to the place where it's actually used, so we don't need to pay the import time if we're not using it. This helps for all Python versions.So for example, only importing
PIL.Imageon Python 3.14:Lazy
Python 3.15 introduces lazy imports:
If you put the new
lazykeyword before an import, it won't be actually imported until first use. Butlazyis only for 3.15+, it's a syntax error in 3.10-3.14.So the second commit introduces
__lazy_modules__.We can only put top-level imports here, and not those from function or in
try/except. And we don't need to put any that used right away at the top-level, likelogginginImage.py, but it's not a big deal if we do.Repeating with 3.15: