Repository navigation
fix(images): keep cached images independent of node-held objects - #10138
Merged
joshistoast merged 3 commits intoNov 3, 2026
Merged
joshistoast merged 3 commits into
joshistoast merged 3 commits into
Conversation
get_pil now copies when a mode conversion fails, and disk storage caches its own copy on save instead of the caller's object. Adds offline compatibility tests running six unmodified third-party PIL nodes (JPPhoto cinematic-effects-pack, MIT) through real image services.
Pfannkuchensack
requested review from
blessedcoolant,
joshistoast and
lstein
as code owners
November 3, 2026 09:48
6 of 7 tasks
joshistoast
requested changes
Nov 3, 2026
save now replaces cached entries for the image and its thumbnail and bumps the cache generation, so an overwrite is visible to later reads and a read that started before it cannot cache the old file.
JPPhoto
approved these changes
Nov 3, 2026
joshistoast
approved these changes
Nov 3, 2026
joshistoast
enabled auto-merge (squash)
November 3, 2026 22:11
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
The image file cache could hand out the same Pillow object that a node holds and keeps mutating:
context.images.get_pil(name, mode)returned the service's cached object itself whenimage.convert(mode)raisedValueError(e.g. a LAB image requested asL). A node that then drew on its result changed what every later reader of that image saw.DiskImageFileStorage.savecached the caller's own object and replaced itsinfo. A node that saves several frames from one canvas in a loop got every cached entry pointing at the last frame until eviction, while the PNGs on disk were correct.Both paths now hand out / cache an independent copy. The conversion-failure warning and the fallback to the original mode are unchanged.
To pin the custom-node contract these paths serve, the PR adds offline compatibility tests that run six unmodified third-party PIL nodes from JPPhoto/cinematic-effects-pack (MIT, pinned to
4a4dfa77ec8c57968281f8162a685d1d89a1aaea) through the real invocation image interface, real image services, in-memory SQLite and disk storage undertmp_path: Chromatic Aberration, Lens Vignette, Lens Blur, Curves, Film Grain and Spherical Distortion, RGB and RGBA each, plus L and LA for Curves. The vendored files are byte-identical to upstream, kept with LICENSE and PROVENANCE.md undertests/app/invocations/third_party_cinematic_effects/, and excluded from ruff. The pack is registered under its own module name and unregistered after the module, so nothing leaks into other test files.Upstream node behavior is recorded as baseline, not changed: Lens Blur and Film Grain return opaque RGB(A), Lens Blur needs an aperture image despite its optional declaration, Curves widens L/LA to RGB/RGBA.
Related Issues / Discussions
None.
QA Instructions
uv run --no-sync pytest tests/app/services/shared/test_invocation_context_images.py tests/app/services/image_files/test_image_files_disk.py tests/app/invocations/test_third_party_image_nodes.py— each file passes alone (30, 68 + 10 skipped, 17).uv run --no-sync pytest -n 4 tests/app/services/images tests/app/services/image_files tests/app/routers/test_custom_nodes.py tests/test_nodes.py tests/app/services/shared/test_invocation_context_images.py tests/app/invocations/test_third_party_image_nodes.py— 213 passed, 11 skipped.Ltest and the save-isolation test fail without the respective fix.ruff check .andruff format --check .clean.Review
Resolved: the save-side aliasing was found in review and fixed here; the fixture's collision guard now checks all eight invocation types the pack registers (Film Grain registers three); an assertion that could not fail and a duplicate mocked test were removed. No remaining known risks; the only cost is one image copy per save.
Checklist
What's Newcopy (if doing a release after this PR)Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.